Vec::spare_capacity_mut¶
Level: reference · for working programmers
One line: The gap between length and capacity, as writable uninitialised memory.
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::set_len— the second half, and the unsafe oneVec::as_mut_ptr— the same buffer as a raw pointerVec::capacity— where the region endsVec::with_capacity— how the region comes to exist