Vec::split_off¶
Level: reference · for working programmers
One line: Cut the vector in two at at, returning the tail.
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::append— the inverse: two vectors into oneVec::drain— taking a range out without splittingVec::truncate— when the tail can just be droppedslice::split_at↗ — the borrowing version, and free