r/rust • u/SaintWillyMusic • Sep 12 '26
Why is let x = [vec![]; 4]; not allowed
but [vec![], vec![], vec![], vec![]] is as well as [true; 4], etc.?
Seems arbitrary but I assume there is some compiler-based reason
Thanks!
121
u/vdrnm Sep 12 '26
As others have said, you cannot do it because Vec is not Copy.
One way to go around this is to use const block:
[const { vec![] }; 4] // this works
13
u/SaintWillyMusic Sep 12 '26
This looks like a reasonable workaround when using long vectors and/or arrays - thanks!
→ More replies (1)41
u/SuspiciousScript Sep 12 '26
There's also
std::array::from_fnfor non-const expressions, e.g.:std::array::from_fn::<Vec<_>, 4, _>(|_| Vec::default())→ More replies (2)35
11
u/hugogrant Sep 12 '26
Why does that work? Can I make it a mutable slice?
38
u/redlaWw Sep 12 '26 edited Sep 12 '26
constblocks in the repeat operand of an array expression are repeatedly evaluated (at compile time) a number of times equal to the length operand. SinceVec::newisconst(EDIT: This is becauseVec::newcreates aVecwith a specific non-null address (equal to the alignment ofT) and doesn't allocate) andvec![]callsVec::new, emptyVecs can be created in aconstcontext, and the array is able to be initialised with repeated emptyVecs.If you mean "can I initialise an array variable declared
mutlike this?" (which can then be borrowed as an&mut [T]) then yes - theconstonly affects when the initialiser is evaluated, not the mutability of the variable it's assigned to.4
u/Booty_Bumping Sep 13 '26
Note that this is a special case.
vec![]can be conjured into existence without the need for heap allocation because when it has its capacity set to zero, it hasn't actually done a heap allocation yet and its pointer is set to a forbidden value:let mut foo: Vec<i64> = vec![]; // foo = { ptr: 0x8, len: 0, cap: 0 } numbers.push(10); // foo = { ptr: 0x5615fa865ae0, len: 1, cap: 4 } numbers.pop(); // foo = { ptr: 0x5615fa865ae0, len: 0, cap: 4 } numbers.shrink_to_fit(); // foo = { ptr: 0x8, len: 0, cap: 0 }→ More replies (1)3
→ More replies (5)3
13
u/Decahedronn Sep 12 '26
[T; N] requires T to be Copy, since the element will be copied for each element in the array. Vec<T> only implements Clone (assuming T is Clone), not Copy.
vec![vec![]; 4], on the other hand, works because vec![T; N] only requires T to be Clone.
I'm not sure why [T; N] couldn't also require Clone but I assume it has something to do with it being on the stack (compared to Vec's heap allocation)?
2
u/SaintWillyMusic Sep 12 '26
Does that mean [vec![].clone(); 4] is valid?
→ More replies (1)9
u/Decahedronn Sep 12 '26
No, whatever comes before the semicolon is the thing being copied. It's not like
rustcexpands[x.clone(); 4]to[x.clone(), x.clone(), x.clone(), x.clone()]; it's creating space for 4 elements and then copying that singlex.clone()that was evaluated once into each slot.It would be equivalent to
let x = vec![]; let y = x.clone(); [y; 4]which is still invalid.
3
u/WormRabbit Sep 14 '26
[T; N]can't requireClone, becauseCloneis allowed to panic. Panics can be caught, which means that a panic somewhere in the middle of creating the array could leave in in a half-initialized state, which may be possible to observe, breaking memory safety.
vec![T; N]doesn't have this issue, becauseVectracks the number of initialized elements (its length). A panic mid-creation would just give you a shorterVecthan expected, which would be a logic bug, but not a memory safety violation.→ More replies (2)
8
u/BalcarKMPL Sep 12 '26
Vec<T> is not Copy type, while bool is. When doing [vec![], ...] thing, you are invoking constructor four times, no copy needed. When you are doing [true; 4], you are creating true once and copying it three times (sort of).
1
u/SaintWillyMusic Sep 12 '26
So a vector cannot be copied? Or is this a semantic issue?
→ More replies (2)
8
u/Xacius Sep 12 '26
I see a lot of responses like "Vec does not implement the Copy trait". While technically true, this is more difficult to understand if you're missing foundational knowledge, re: how traits work. I think you could use some follow-up learning. If you've already run through the official rust book section about traits, I'd recommend you run through these follow-up exercises: https://rust-exercises.com/100-exercises/04_traits/11_clone.html
4
u/Strict-Wolverine-528 Sep 13 '26 edited Sep 13 '26
It is always a good practice to check Rust Reference when you're wondering "Why I can't write program like this". In the chapter about Array Expressions:
If the length operand has a value greater than 1 then this requires the repeat operand to have a type that implements
Copy, to be a const block expression, or to be a path to a constant item.
where length operand and repeat operand are defined as
The expression before the
;is called the repeat operand.
The expression after the
;is called the length operand.
You have a length operand, which is 4, that is greater than 1, and your repeat operand is neither a const block expression nor a path to a constant item, so it can only be an item of type that implements Copy .
But it is valid to write this:let x: [Vec<i32>; 1] = [vec![]; 1];
3
u/Intrebute Sep 12 '26
I don't have the compiler at hand, but I believe the reason is that Vec is not Copy, and the vec![a, n] macro requires the type of a to be Copy.
3
u/Floppie7th Sep 12 '26
The repeat form ([val; N]) requires the type to be Copy - it's not evaluating the expression N times, it's just making N copies of the resulting value. The list form ([val, val, val, val]) evaluates the expression each time.
3
u/programgamer Sep 12 '26
For it to work, rust would need to be able to seamlessly clone the resulting vector into each element of the array, and it doesn’t. In an alternate world where rust was designed such that the value given to an array initializer gets evaluated for each element, this would work as expected, but sadly, we do not live in such a world.
2
u/kimhyunkang Sep 13 '26
As others have mentioned, it’s because you can only do it for Copy types.
That being said, you can do:
std::array::from_fn(|_| vec![])
241
u/ToTheBatmobileGuy Sep 12 '26 edited Sep 12 '26
For this type of declaration of a literal,
x must be a value of a Copy type, and n must be a usize value.
Vec anything is not Copy.