3

I want to understand why in Rust, we can't create an array with a non-constant size?

Essentially - the piece of code here looks similar (to me). But with vector I can change the length of the vector but can't do that with array. So, why create this artificial limitation:

fn do_something(a: &mut [usize]) {
    for i in 0..a.len() {
        println!("{}", a[i]);
    }
    println!();
}

pub fn main() {
    let capacity: usize = 5;
    let mut v: Vec<usize> = Vec::with_capacity(capacity);
    for i in 0..capacity {
        v.push(i);
    }
    do_something(v.as_mut_slice());
    v.push(11);
    do_something(v.as_mut_slice());

    const CONST_CAPACITY: usize = 5;
    let mut arr: [usize; CONST_CAPACITY] = [0; CONST_CAPACITY];
    do_something(&mut arr);
}

Playground

Is it possible to have stack allocated arrays with the size determined at runtime in Rust? - asks a similar question but to any one searching, it is hard to search answer to this question. Perhaps edit the question title slightly to help increase the visibility while searching (and mark this as dupe) or keep both.

Coder
  • 1,415
  • 2
  • 23
  • 49
  • 1
    Does this answer your question? [Is it possible to have stack allocated arrays with the size determined at runtime in Rust?](https://stackoverflow.com/questions/27859822/is-it-possible-to-have-stack-allocated-arrays-with-the-size-determined-at-runtim) – Brian61354270 Jun 28 '22 at 20:39
  • 5
    I think question either boils down to "[what are the stack and heap?](https://stackoverflow.com/q/79923/11082165)" or "why are variable length stack-allocated arrays problematic". The latter question is partially addressed in the dupe. You may also find this cross-language duplicate informative: [Why aren't variable-length arrays part of the C++ standard?](https://stackoverflow.com/q/1887097/11082165) – Brian61354270 Jun 28 '22 at 20:43
  • Typicall all variables are allocated "on the stack", which is a very limited chunk of memory (e.g. 8 MB is typical for Linux). `vec` only allocates a bunch of pointers "on the stack" and allocates the data separately, "on the heap", which is not limited. – yeputons Jun 28 '22 at 20:43
  • 1
    @yeputons The bigger problem is that stack frames have a specific size to hold the locals for a given function. If you allocate on the stack you have to resize the function's stack frame, which is _possible,_ but problematic if the array is put between two other values, for example. – cdhowie Jun 28 '22 at 20:50
  • 1
    Thanks for the response. I think the answer that arrays are stack-based and vector is heap-based, explains it. The other similar questions do not "ask" the basic question but start with why the solution is not feasible. Hence I had a hard time search it (and possibly others might as well). – Coder Jun 28 '22 at 22:14

1 Answers1

3

Generally speaking, everything in Rust must have a known size (in bytes). In situations where a thing does not have a known size, you may only do very specific things with it. An array of size n and element type T takes up n * size_of::<T>() space (Note: size_of is a function in Rust). If you like thinking in terms of implementations, you can think of this as being because we need to know how much stack space to allocate for the data. A variable-sized type uses a variable amount of stack space, which is problematic.

The fundamental way around this is Box. A Box is a smart pointer (like a std::unique_ptr in C++), and it contains a value whose size may or may not be known. Again, in terms of implementation, it does so by storing the value on the heap, which is a giant chunk of memory you can go to and ask the OS for some (variable) amount of bytes out of. Data structures with dynamic size are fine in this case, since the OS only needs to know the size at runtime. This gets used all the time for things like trait objects, whose sizes aren't known.

The type Box<[u32]> (Note the lack of a size) is a valid type, but still not a useful one for our purposes. We can assign it a Box containing an array of any size, but we still can't really get a runtime-known size (the array's size had to be known at some point by some function in order to allocate it). Rust doesn't really provide a blanket "give me an array of runtime size" function like C++'s new[]. Instead, the Vec type uses low-level primitives that act on *T (as in, unsafe C-style pointers) in order to manage the memory for you. If you want the nitty gritty details on how this works, there's a great chapter of the Rustonomicon on implementing your own Vec-like type. But the basic answer is: Rust provides low-level (often unsafe) primitives that Vec knows how to use, and you can use those too.

But you don't often need to. Whereas C++ is riddled with opponents to STL and (in places) questionable decisions (vector<bool> comes to mind), Rust's Vec is pretty uncontroversial. It's like an array but dynamically-sized, and everybody uses it.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116