Vec::set_len¶
Level: reference · for working programmers
One line: Set the length field directly. Initialises nothing, drops nothing, checks nothing.
Stable since 1.0.0. Its const form is still unstable.
Every guarantee here is yours to keep. The two conditions, both unchecked:
new_len <= capacity()- every element below
new_lenis initialised
Break either and it is undefined behaviour, not a panic.
Shrinking with it leaks. The elements above the new length are never dropped — no destructor, no deallocation of anything they own. That is the difference from truncate, which drops them properly, and the example below shows two Noisy values silently not being dropped.
set_len(0) is the one call that is always sound, since 0 <= capacity() always holds. (It still leaks whatever was there.)
The legitimate use is the second half of the spare_capacity_mut / as_mut_ptr pattern: you wrote n elements into the buffer, so you claim n. Growing back into a region you have genuinely initialised is also fine.
The safe alternatives, in order of preference: truncate, clear, resize, extend.
Example¶
vec_set_len.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.
fn main() {
// set_len writes the length field. It initialises nothing, drops nothing,
// and checks nothing — every guarantee is yours to keep.
let mut v: Vec<u8> = Vec::with_capacity(4);
let spare = v.spare_capacity_mut();
spare[0].write(7);
spare[1].write(8);
unsafe { v.set_len(2) }; // 2 because 2 slots were written
println!("{v:?}");
// Shrinking with it LEAKS the elements above the new length: they are
// never dropped. That is the difference from truncate().
struct Noisy(u8);
impl Drop for Noisy {
fn drop(&mut self) { println!(" dropping {}", self.0); }
}
{
let mut v = vec![Noisy(1), Noisy(2), Noisy(3)];
println!("truncate(1):");
v.truncate(1);
println!(" and now the survivor:");
}
{
let mut v = vec![Noisy(4), Noisy(5), Noisy(6)];
println!("set_len(1):");
unsafe { v.set_len(1) };
println!(" 5 and 6 were never dropped — that is a leak");
println!(" and now the survivor:");
}
// The two conditions, both unchecked:
// new_len <= capacity()
// every element below new_len is initialised
let mut v: Vec<u32> = Vec::with_capacity(10);
println!("capacity {} — set_len(11) here would be UB", v.capacity());
unsafe { v.set_len(0) }; // always sound: 0 <= capacity
println!("set_len(0) is the one that is always safe: {v:?}");
// Growing back into a region you HAVE initialised is the legitimate use.
let mut v: Vec<u8> = Vec::with_capacity(4);
v.extend_from_slice(&[1, 2, 3, 4]);
unsafe { v.set_len(2) }; // hide the tail
println!("hidden: {v:?}");
unsafe { v.set_len(4) }; // reveal it again — still initialised
println!("revealed: {v:?}");
// The safe alternatives, in order of preference:
// truncate() shrink and drop
// clear() truncate(0)
// resize() grow with a value
// extend() grow from an iterator
let mut v = vec![1, 2, 3, 4];
v.truncate(2);
v.resize(4, 0);
println!("no unsafe needed: {v:?}");
}
Verified output of vec_set_len.rs — regenerated by tools/run_examples.py, never hand-typed.
[7, 8]
truncate(1):
dropping 2
dropping 3
and now the survivor:
dropping 1
set_len(1):
5 and 6 were never dropped — that is a leak
and now the survivor:
dropping 4
capacity 10 — set_len(11) here would be UB
set_len(0) is the one that is always safe: []
hidden: [1, 2]
revealed: [1, 2, 3, 4]
no unsafe needed: [1, 2, 0, 0]
See also¶
Vec::truncate— shrink and drop, which is what you usually wantVec::spare_capacity_mut— the safe half of the patternVec::as_mut_ptr— the other way to write into the bufferVec::len— the field this writes