Maybe I am blind and can't find it but why is there no constructor that does the following? I feel like from an optimization standpoint it is useful to not spend time initializing the vector with 0s or anything when it is not necessary.
let mut vec = Vec::with_capacity(length);
unsafe {
vec.set_len(length);
}
depending on what the element type is, there are different ways:
// if the type is `Clone`, just use the `vec![]` macro
// alternatively, use `resize()`
let v = vec![value; len];
// if the type is not `Clone`, use `resize_with()`
let mut v = Vec::with_capacity(len);
v.resize_with(len, Value::constructor);
The most common correct way of using set_len is to first initialize the elements, and then calling set_len, or alternatively do the same thing but just working with uninitialized memory without calling set_len. In any case I don't see situations where calling set_len without first initializing the elements (like you're proposing) is reasonable.
I need it for coding puzzles where you iteratively initialize a vec from both sides at the same time.
For example in this exercise (LeetCode Top Interview 150 Trapping Rain Water)
impl Solution {
pub fn trap(height_map: Vec<i32>) -> i32 {
let mut water_map = Vec::with_capacity(height_map.len());
unsafe {
water_map.set_len(height_map.len());
}
let mut l_wall = 0;
let mut r_wall = 0;
let mut sum = 0;
for i in 0..height_map.len() {
let l = i;
let r = height_map.len() - 1 - i;
let l_height = height_map[l];
let r_height = height_map[r];
let l_capacity = (l_wall - l_height).max(0);
let r_capacity = (r_wall - r_height).max(0);
if l < r {
water_map[l] = l_capacity;
water_map[r] = r_capacity;
} else if l == r {
if l_capacity < r_capacity {
sum += l_capacity;
// unnecessary because we only care about the sum
// water_map[l] = l_capacity;
} else {
sum += r_capacity;
// water_map[l] = r_capacity;
}
} else {
if l_capacity < water_map[l] {
sum += l_capacity;
// water_map[l] = l_capacity;
} else {
sum += water_map[l];
}
if r_capacity < water_map[r] {
sum += r_capacity;
// water_map[r] = r_capacity;
} else {
sum += water_map[r];
}
}
if l_wall < l_height {
l_wall = l_height;
}
if r_wall < r_height {
r_wall = r_height;
}
}
sum
}
}
that's just premature optimization. just create the Vec initialized with zero.
- let mut water_map = Vec::with_capacity(height_map.len());
+ let mut water_map = vec![0; height_map.len()];
note, the "proper" way in rust to handle uninitialized memory is to use MaybeUninit. the way you unsafely set_len() without actually initialize the contents is instant UB.
For the sake of clarity: there is no guarantee that the elements created by set_len will be zeroed. On modern systems, with a freshly created Vec, it's probably zeroed, but still could be random. That's not only UB; even were it defined behavior, it would give your code logic bugs.
Only if it's a fresh allocation from the OS, not a re-used one.
And of course if you want to try to rely on that, that's what vec![0; n] is for -- it's special-cased to ask the allocator for zero'd memory, so if the allocator is getting it from the OS and the allocator knows it's zero'd already, it won't re-zero it and nor will Vec.
EDIT
Thanks @nerditation for your note. My correction here is wrong. For the vec macro Clone actually is enough.
Original Post
That is not quite true. The actual requirement for using the vec! macro is for the element to be Copy. As you can see in the following code snipped of struct Foo which implements Clone but not Copy:
#[derive(Clone)]
struct Foo;
fn main() {
let f = Foo;
let v = [f; 200];
}
Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `Foo: Copy` is not satisfied
--> src/main.rs:7:14
|
7 | let v = [f; 200];
| ^ the trait `Copy` is not implemented for `Foo`
|
= note: the `Copy` trait is required because this value will be copied for each element of the array
= help: consider using `core::array::from_fn` to initialize the array
= help: see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information
help: consider annotating `Foo` with `#[derive(Copy)]`
|
2 + #[derive(Copy)]
3 | struct Foo;
|
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` (bin "playground") due to 1 previous error
If your initialization value is const constructible you can circumvent this problem by creating a constant and using that instead. This will work:
#[derive(Clone)]
struct Foo;
fn main() {
const F: Foo = Foo;
let v = [F; 200];
}
I find it confusing that [Foo; 200] doesn't work, since Foo is an "implicit constant" according to the reference, so I'd think it's equivalent to F. But apparently not.
Modern allocators tend to group by size buckets, so for something small if you allocate it, set the value, deallocate it, allocate again, and read it, it's likely that you'll just get what you'd written before because the allocator never gave it back to the OS, just kept that page of small allocations around.