Skip to content

Vec::reserve_exact

Vec methods · Collections

Level: reference · for working programmers

One line: Reserve without the speculative slack — len + additional, and no more.

pub fn reserve_exact(&mut self, additional: usize)

Stable since 1.0.0.

Same arithmetic as reserveadditional 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_exact in the standard library ↗