Vec<T> in Depth
drain() and splice()
The .drain(range) and .splice(range, replace_with) methods remove a contiguous range of elements from a Vec<T> and return an iterator that yields the removed elements by value (T).
Signatures and Behavior
impl<T> Vec<T> {
pub fn drain<R>(&mut self, range: R) -> Drain<'_, T>
where
R: RangeBounds<usize>;
pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter>
where
R: RangeBounds<usize>,
I: IntoIterator<Item = T>;
}
| Method | What It Removes | What It Inserts in Place of range |
When Does the Shift Happen? |
|---|---|---|---|
v.drain(range) |
Elements in range (yielded by Drain<'_, T>) |
Nothing | When Drain is dropped (even if partially consumed!) |
v.splice(range, iter) |
Elements in range (yielded by Splice) |
All elements from replace_with (can have different length than range) |
When Splice is dropped |
let mut v = vec![10, 20, 30, 40, 50];
1. v.drain(1..4) ──> yields 20, 30, 40; leaves v = [10, 50]
2. v.splice(1..4, [99, 100])
──> yields 20, 30, 40; replaces slice with [99, 100] -> v = [10, 99, 100, 50]
Extracting a Subrange by Value with drain()
Unlike .into_iter(), which consumes the entire Vec<T> and frees its heap allocation, .drain(range) moves out only the specified range (or .. for all elements) and preserves v's allocated capacity:
fn main() {
let mut queue = vec![
String::from("task-1"),
String::from("task-2"),
String::from("task-3"),
String::from("task-4"),
];
// Move the first 2 tasks out into a new Vec; `queue` retains ["task-3", "task-4"]:
let batch: Vec<String> = queue.drain(0..2).collect();
println!("Drained batch: {batch:?}");
println!("Remaining queue: {queue:?} (cap={})", queue.capacity());
}
Drained batch: ["task-1", "task-2"]
Remaining queue: ["task-3", "task-4"] (cap=4)
Notice that even if you do not consume the Drain iterator (let _ = queue.drain(0..2);), dropping the Drain struct automatically drops any unconsumed elements in 0..2 and shifts the remaining tail left.
Replacing a Subrange in One Operation with splice()
v.splice(range, replace_with) replaces range with any IntoIterator<Item = T>, shifting the tail of v at most once when the Splice iterator is dropped:
fn main() {
let mut tokens = vec!["SELECT", "*", "FROM", "users"];
// Replace "*" (index 1..2) with three column names, and collect the replaced item:
let removed: Vec<&str> = tokens
.splice(1..2, ["id", "email", "created_at"])
.collect();
println!("Removed: {removed:?}");
println!("Updated: {tokens:?}");
}
Removed: ["*"]
Updated: ["SELECT", "id", "email", "created_at", "FROM", "users"]