Skip to content

Vec::spare_capacity_mut

Vec methods · Collections

Level: reference · for working programmers

One line: The gap between length and capacity, as writable uninitialised memory.

pub fn spare_capacity_mut(&mut self) -> &mut [MaybeUninit<T>]

Stable since 1.60.0.

Returns &mut [MaybeUninit<T>] covering capacity() - len() slots. Writing into it does not change the length: you fill the memory, then tell the vector how much you filled with set_len. That two-step split is the whole design — the safe half hands you the region, the unsafe half makes the claim about it.

MaybeUninit<T> is the same size and alignment as T, so the region really is the buffer, not a copy.

The danger is entirely in the second step. Getting the count wrong is undefined behaviour, not a panic — set_len past what you actually initialised means the vector hands out references to uninitialised memory, and nothing checks it.

This exists for wrapping code that writes into a buffer and reports how much it wrote — a C API, a decompressor, a syscall. If you control the writer, extend, resize and extend_from_slice do the same job with no unsafe at all, and are what you should reach for first.

Example

vec_spare_capacity_mut.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.

use std::mem::MaybeUninit;

fn main() {
    // The gap between len and capacity, typed as uninitialised memory.
    let mut v: Vec<u32> = Vec::with_capacity(10);
    v.push(1);
    println!("len {} cap {} spare {}", v.len(), v.capacity(), v.spare_capacity_mut().len());

    // Writing into it does NOT change the length. That is the whole contract:
    // you fill the memory, then you tell the Vec how much you filled.
    let mut v: Vec<u32> = Vec::with_capacity(4);
    let spare = v.spare_capacity_mut();
    spare[0].write(10);
    spare[1].write(20);
    println!("written but len is still {}", v.len());
    unsafe { v.set_len(2) };
    println!("after set_len(2): {v:?}");

    // Get it wrong and it is undefined behaviour, not a panic — set_len past
    // what you actually initialised reads uninitialised memory. This is the
    // one place a Vec asks you to be right without checking.
    let mut v: Vec<u8> = Vec::with_capacity(8);
    let spare = v.spare_capacity_mut();
    for (i, slot) in spare.iter_mut().enumerate().take(3) { slot.write(i as u8 * 5); }
    unsafe { v.set_len(3) };          // 3, because 3 is what was written
    println!("{v:?}");

    // The safe way to do the same thing, which is what you should reach for
    // unless you are wrapping a C API that writes into a buffer.
    let mut v: Vec<u8> = Vec::with_capacity(8);
    v.extend((0..3).map(|i| i * 5));
    println!("the safe spelling: {v:?}");

    // An empty spare region when the vector is full.
    let mut v = vec![1u8, 2, 3];
    v.shrink_to_fit();
    println!("full vector has {} spare slots", v.spare_capacity_mut().len());

    // MaybeUninit<T> is the same size as T — the region really is the buffer.
    println!("size_of MaybeUninit<u32> == size_of u32: {}",
             size_of::<MaybeUninit<u32>>() == size_of::<u32>());
}

Verified output of vec_spare_capacity_mut.rs — regenerated by tools/run_examples.py, never hand-typed.

len 1 cap 10 spare 9
written but len is still 0
after set_len(2): [10, 20]
[0, 5, 10]
the safe spelling: [0, 5, 10]
full vector has 0 spare slots
size_of MaybeUninit<u32> == size_of u32: true

See also

Vec::spare_capacity_mut in the standard library ↗