Skip to content

Vec::set_len

Vec methods · Collections

Level: reference · for working programmers

One line: Set the length field directly. Initialises nothing, drops nothing, checks nothing.

pub unsafe fn set_len(&mut self, new_len: usize)

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_len is 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::set_len in the standard library ↗