Vec::reserve_exact¶
Level: reference · for working programmers
One line: Reserve without the speculative slack — len + additional, and no more.
Stable since 1.0.0.
Same arithmetic as reserve — additional is on top of len() — with the deliberate over-allocation suppressed. On an empty Vec<u32>, reserve(1) gives capacity 4 and reserve_exact(1) gives capacity 1.
"Exact" is still a request. The allocator may hand back more, so the contract remains capacity() >= len + additional. Do not build logic on capacity() == len().
Reach for it when the vector is filled once and then kept — a snapshot, a parsed record, a buffer handed to something that will hold it a long time. Slack that will never be used is memory you are not returning.
Do not reach for it in a push loop. Calling reserve_exact(1) before every push replaces amortised doubling with a reallocation on every single element: the run below measures 8 reallocations for 8 pushes against 2 for plain push. That is the growth strategy undone by hand, and it turns n pushes from O(n) into O(n²) copying.
Example¶
vec_reserve_exact.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.
fn main() {
// Asks for exactly len + additional, with no speculative slack.
let mut v = vec![1, 2, 3];
v.reserve_exact(7);
println!("len {} capacity {}", v.len(), v.capacity());
// reserve() on the same vector is allowed to round up.
let mut a: Vec<u32> = Vec::new();
let mut b: Vec<u32> = Vec::new();
a.reserve(1);
b.reserve_exact(1);
println!("reserve(1) -> {} reserve_exact(1) -> {}", a.capacity(), b.capacity());
// "Exact" is a request, not a guarantee — the allocator may still hand
// back more, so the contract is still `capacity() >= len + additional`.
let mut v: Vec<u8> = Vec::new();
v.reserve_exact(5);
println!("asked exactly 5, got at least 5: {}", v.capacity() >= 5);
// Where it belongs: a vector that is filled once and then kept. Slack
// that will never be used is just wasted memory.
let source = [1u8, 2, 3, 4, 5];
let mut snapshot: Vec<u8> = Vec::new();
snapshot.reserve_exact(source.len());
snapshot.extend_from_slice(&source);
println!("snapshot len {} cap {}", snapshot.len(), snapshot.capacity());
// Where it does NOT belong: a vector still being pushed into. Exact
// reservations before each push give back the growth-by-doubling that
// makes pushing amortised O(1).
let mut v: Vec<u32> = Vec::new();
let mut reallocs = 0;
for n in 0..8 {
let before = v.capacity();
v.reserve_exact(1);
v.push(n);
if v.capacity() != before { reallocs += 1; }
}
println!("reserve_exact before every push: {reallocs} reallocations for 8 pushes");
let mut v: Vec<u32> = Vec::new();
let mut reallocs = 0;
for n in 0..8 {
let before = v.capacity();
v.push(n);
if v.capacity() != before { reallocs += 1; }
}
println!("plain push: {reallocs} reallocations for 8 pushes");
}
Verified output of vec_reserve_exact.rs — regenerated by tools/run_examples.py, never hand-typed.
len 3 capacity 10
reserve(1) -> 4 reserve_exact(1) -> 1
asked exactly 5, got at least 5: true
snapshot len 5 cap 5
reserve_exact before every push: 8 reallocations for 8 pushes
plain push: 2 reallocations for 8 pushes
See also¶
Vec::reserve— the one to use while a vector is still growingVec::try_reserve_exact— the same, returningResultVec::shrink_to_fit— the after-the-fact version of the same intent