Rust Drain, Splice & Extract
drain, splice and extract_if are Vec tools that remove or replace whole stretches at once. drain takes a range out.
drain takes out a whole range
drain(range) gives back an iterator over the removed items. Collect it, and the gap closes behind them:
let mut v = vec![1, 2, 3, 4, 5];
let mid: Vec<i32> = v.drain(1..4).collect();
println!("took = {:?} left = {:?}", mid, v);splice swaps a range for new items
splice(range, new_items) replaces a stretch and hands the old stretch back as an iterator:
let mut v = vec![1, 2, 3];
let old: Vec<i32> = v.splice(1..2, [9, 8]).collect();
println!("v = {:?} old = {:?}", v, old);An empty range makes splice a pure insert
Give splice an empty range and nothing is removed — the new items simply move in at that point:
let mut v = vec![1, 4];
let none: Vec<i32> = v.splice(1..1, [2, 3]).collect();
println!("v = {:?} removed = {:?}", v, none);extract_if pulls out what matches
extract_if walks the Vec and takes out every item the closure accepts, handing those items to you:
let mut v = vec![1, 2, 3, 4, 5, 6];
let evens: Vec<i32> = v.extract_if(.., |x| *x % 2 == 0).collect();
println!("evens = {:?} odds left = {:?}", evens, v);Example
fn main() {
let mut songs = vec!["intro", "ad", "song a", "ad", "song b", "outro"];
let ads: Vec<&str> = songs.extract_if(.., |s| *s == "ad").collect();
println!("removed = {:?} playlist = {:?}", ads, songs);
let swapped: Vec<&str> = songs.splice(0..1, ["cold open"]).collect();
println!("swapped out = {:?} now = {:?}", swapped, songs);
let tail: Vec<&str> = songs.drain(2..).collect();
println!("tail = {:?} left = {:?}", tail, songs);
}Key points
- drain, splice and extract_if all hand back iterators.
- collect() the result into a Vec when you need the removed items as a list
- The returned iterator holds a mutable borrow of the Vec.
- It holds it until the iterator is used up or dropped.
- The Vec is off-limits in between
- If a drain iterator is dropped before it is finished, the rest of its range is still removed.
- drain always completes the removal
- splice accepts any iterator of new items, so the replacement may be longer or shorter than the stretch it replaces
- extract_if hands the closure a &mut, so the test may also change an item it decides to keep
In this example, a playlist is cleaned in three moves. extract_if walks the whole list and pulls out both "ad" entries, leaving the four real tracks. splice then replaces position 0..1 — just the old intro — with "cold open", and hands the replaced intro back. Finally drain(2..) cuts everything from position 2 onward into tail, leaving the two opening tracks. Each tool moved a whole stretch at once. No loop shifted items one by one.
Note
Calling remove(i) in a loop shifts the tail again and again.
For taking out a whole stretch, drain and extract_if move the tail once.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
What does splice give back?