Skip to content

Vec::split_off

Vec methods · Collections

Level: reference · for working programmers

One line: Cut the vector in two at at, returning the tail.

pub fn split_off(&mut self, at: usize) -> Vec<T, A>
where
    A: Clone,

Stable since 1.4.0.

self keeps [0, at) and the returned vector gets [at, len). at == 0 moves everything out; at == len() returns an empty vector and changes nothing. Anything above len() panics.

The elements are moved, so T need not be Clone.

The tail is a new allocation, and self keeps its original buffer — so this is not a free split. For read-only chunking, the slice methods chunks, split_at and windows do the same partitioning with no allocation and no moving at all.

Where it earns its keep is when the two halves must be owned separately — sent to different threads, returned from a function, stored in different places.

Example

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

fn main() {
    // Everything from `at` onwards leaves in a new Vec; self keeps the front.
    let mut v = vec![1, 2, 3, 4, 5];
    let tail = v.split_off(2);
    println!("head {v:?}  tail {tail:?}");

    // at == 0 moves everything out and leaves self empty.
    let mut v = vec![1, 2];
    let all = v.split_off(0);
    println!("all {all:?}  v {v:?}");

    // at == len() gives an empty tail and changes nothing.
    let mut v = vec![1, 2];
    let none = v.split_off(2);
    println!("v {v:?}  tail {none:?}");

    // The tail is a NEW allocation; self keeps its original buffer.
    let mut v: Vec<u8> = Vec::with_capacity(64);
    v.extend_from_slice(&[0; 64]);
    let tail = v.split_off(32);
    println!("head cap {} tail cap {}", v.capacity(), tail.capacity());

    // The elements are moved, so non-Clone works.
    let mut owners = vec![String::from("a"), String::from("b"), String::from("c")];
    let back = owners.split_off(1);
    println!("{owners:?} + {back:?}");

    // Chunking a vector into fixed-size pieces, front to back.
    let mut v: Vec<u8> = (1..=7).collect();
    let mut chunks = vec![];
    while v.len() > 3 { let rest = v.split_off(3); chunks.push(v); v = rest; }
    chunks.push(v);
    println!("{chunks:?}");

    // For read-only chunking, `chunks()` on the slice does this without
    // moving anything at all.
    let v: Vec<u8> = (1..=7).collect();
    println!("{:?}", v.chunks(3).collect::<Vec<_>>());

    let hook = std::panic::take_hook();
    std::panic::set_hook(Box::new(|_| {}));
    let caught = std::panic::catch_unwind(|| { let mut v = vec![1]; let _ = v.split_off(9); });
    std::panic::set_hook(hook);
    println!("split_off past len panicked: {}", caught.is_err());
}

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

head [1, 2]  tail [3, 4, 5]
all [1, 2]  v []
v [1, 2]  tail []
head cap 64 tail cap 32
["a"] + ["b", "c"]
[[1, 2, 3], [4, 5, 6], [7]]
[[1, 2, 3], [4, 5, 6], [7]]
split_off past len panicked: true

See also

Vec::split_off in the standard library ↗