Vec::with_capacity¶
Level: reference · for working programmers
One line: An empty vector with room for n elements already allocated.
Stable since 1.0.0. Its const form is still unstable.
The vector is still empty — len() is 0 and indexing it panics. All that exists is the buffer. This is the usual misreading of the name: Vec::with_capacity(10) is not ten zeroes, it is room for ten of something. For ten zeroes, write vec![0; 10].
Use it when you know roughly how many elements are coming. Nine pushes into a Vec::new() reallocate three times, copying everything stored so far on each one; nine pushes into Vec::with_capacity(9) allocate once and copy nothing.
"At least" is the contract. You may get more than you asked for, never less. And capacity is not a limit: the eleventh push into a with_capacity(10) reallocates like any other push — it does not fail.
Reaching for it without a number in hand is not an optimisation. Guessing high wastes memory that shrink_to_fit then has to hand back; guessing low buys nothing. When the count comes from an iterator, collect already does this whenever the iterator knows its own length.
A capacity that would exceed isize::MAX bytes panics rather than returning; try_reserve is the fallible route.
Example¶
vec_with_capacity.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.
fn main() {
// Capacity is a promise about the buffer, not about the contents.
let v: Vec<i32> = Vec::with_capacity(10);
println!("len {} cap {} empty {}", v.len(), v.capacity(), v.is_empty());
// Ten pushes, one allocation. Watch the pointer stay put.
let mut sized: Vec<u32> = Vec::with_capacity(9);
let first = sized.as_ptr() as usize;
for n in 1..=9 { sized.push(n); }
println!("moved during 9 pushes: {}", sized.as_ptr() as usize != first);
println!("cap after 9 pushes: {}", sized.capacity());
// Without it, the same nine pushes reallocate three times.
let mut grown: Vec<u32> = Vec::new();
let mut caps = vec![];
for n in 1..=9 {
grown.push(n);
if caps.last() != Some(&grown.capacity()) { caps.push(grown.capacity()); }
}
println!("capacity sequence without with_capacity: {caps:?}");
// "At least": std may give you more than you asked for, never less.
let at_least: Vec<u8> = Vec::with_capacity(5);
println!("asked 5, got at least 5: {}", at_least.capacity() >= 5);
// The 11th push reallocates anyway — capacity is not a limit.
let mut ten: Vec<u8> = Vec::with_capacity(10);
for n in 0..11 { ten.push(n); }
println!("pushed 11 into cap 10: len {} cap {}", ten.len(), ten.capacity());
}
Verified output of vec_with_capacity.rs — regenerated by tools/run_examples.py, never hand-typed.
len 0 cap 10 empty true
moved during 9 pushes: false
cap after 9 pushes: 9
capacity sequence without with_capacity: [4, 8, 16]
asked 5, got at least 5: true
pushed 11 into cap 10: len 11 cap 20
See also¶
Vec::new— when you have no idea how manyVec::reserve— the same reservation on a vector that already existsVec::capacity— reading it backVec::shrink_to_fit— giving back what you over-reserved