'Create an empty vector of size n, and not fill it with anything
I need to initialize a vector for use as a buffer. I don't care what values it contains before I put something in it, so I don't want the program to waste time filling it with zeroes. I know about with_capacity, but it requires me to push() elements, which is inconvenient because I would need to constantly check if I have pushed something to an index before or not.
Basically, I'm looking for an equivalent of this C++ array:
int* arr = new int[size];
arr[2]; // random garbage
Solution 1:[1]
If you cannot fill the vector for performance reasons (but please benchmark!), you need to use unsafe code. You can e.g. use Vec::spare_capacity_mut() (or the nightly Vec::split_at_spare_mut()) to access the elements, then Vec::set_len(). For example:
let mut v = Vec::with_capacity(N);
for (i, item) in v.spare_capacity_mut().iter_mut().enumerate() {
item.write(i);
}
// SAFETY: All elements were initialized.
unsafe {
v.set_len(N);
}
Or you can manually manage the pointers with as_mut_ptr() (less recommended, but may be better if some of the elements are already initialized:
let mut v = Vec::<i32>::with_capacity(N);
// SFAETY: There are enough elements (even if uninitialized).
let all_elements = unsafe {
std::slice::from_raw_parts_mut(v.as_mut_ptr().cast::<std::mem::MaybeUninit<_>>(), N)
};
for (i, item) in all_elements.iter_mut().enumerate() {
item.write(i);
}
// SAFETY: All elements were initialized.
unsafe {
v.set_len(N);
}
Solution 2:[2]
This is hard and, to some extent, anti-idiomatic, because Rust will try to prevent you manipulating uninitialized memory by any means. And, even though it sounds like a limitation if you come from C++, you have to realize that everything does not translate literally from C++ to Rust, even though the two languages share a lot.
For instance, if you:
- don't want to fill a vector with default values,
- don't want
pushvalues while going on,
then you may be better off building an iterable, and collecting it.
You could also fill the vector with Nones.
In particular, Rust incentivizes you not to leave the possibility of a variable pointing to uninitialized memory. That is, even if you achieve (as I will explain later) to have uninitialized memory, Rust will require you to work with unsafe blocks when you access it, until you have "certified" it's all initialized, and even then you are never allowed to read from uninitialized memory, which results in UB (which is the worst could happen).
If you really want to work with uninitialized memory, then read the relevant chapter of the rustnomicon. In the end, what you will use is MaybeUninit. However, read carefully the rustnomicon too (not only the doc page of MaybeUninit), as it contains subtleties.
Solution 3:[3]
If your goal is to store for example integer values, you can efficiently use the vec macro to initialize the vector:
let vec = vec![0; 5];
assert_eq!(vec, [0, 0, 0, 0, 0]);
This form of macro invocation takes two parameters. The first one is the value of the element to insert. The second is the number of copies of the element to insert.
It is equivalent to:
let mut vec = Vec::with_capacity(5);
vec.resize(5, 0);
See https://doc.rust-lang.org/std/vec/struct.Vec.html for more details.
Note that when you say:
waste time filling it with zeroes
you probably want to quantify this. Note that it appears that the operation I described above is O(n) in the general case (see https://doc.rust-lang.org/src/alloc/vec/mod.rs.html#2315) so it might not fit your use case.
If however your vector contains integer values, there is a good chance the operation will be O(1) as per the explanation from this answer. As said elsewhere, please benchmark first.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | BlackBeans |
| Solution 3 |
