#239 Jul 4, 2026

239. Vec::drain — Remove a Range and Keep What You Pulled Out

truncate throws elements away. split_off allocates a second Vec. When you want to remove a range from a Vec and actually use those elements — and keep the rest — drain hands them to you as an iterator and shifts everything else down for you.

The manual remove-and-collect

Say you want to pull a batch out of the front of a queue and process it:

1
2
3
4
5
6
7
8
9
let mut queue = vec![1, 2, 3, 4, 5];

// take the first three, keep the rest
let mut batch = Vec::new();
for _ in 0..3 {
    batch.push(queue.remove(0)); // each remove is O(n): shifts everything left
}
assert_eq!(batch, vec![1, 2, 3]);
assert_eq!(queue, vec![4, 5]);

Every remove(0) shifts the whole tail down one slot — quadratic for a batch, and the intent is buried in a loop.

drain does it in one pass

1
2
3
4
5
6
let mut queue = vec![1, 2, 3, 4, 5];

let batch: Vec<_> = queue.drain(0..3).collect();

assert_eq!(batch, vec![1, 2, 3]);
assert_eq!(queue, vec![4, 5]);

drain(range) removes that range, yields the removed elements in order, and shifts the remaining tail down once. You own the drained values — collect them, iterate them, or pipe them straight into another call.

Drain the whole thing to reuse the allocation

drain(..) empties the Vec but keeps its capacity, so the buffer is ready to refill without reallocating:

1
2
3
4
5
6
7
8
let mut buf = vec![10, 20, 30];
let cap = buf.capacity();

let sum: i32 = buf.drain(..).sum(); // consume every element by value
assert_eq!(sum, 60);

assert!(buf.is_empty());
assert_eq!(buf.capacity(), cap); // allocation retained — refill it next loop

That’s the move-out-and-reuse trick: unlike into_iter(), which consumes the Vec, drain(..) leaves you an empty-but-allocated Vec to keep using.

The removal happens even if you don’t consume it

Drain is a draining iterator: dropping it removes the range regardless of how many items you pulled. So queue.drain(1..4); on its own still deletes that range — you don’t have to .collect() to make it take effect.

1
2
3
let mut v = vec!['a', 'b', 'c', 'd', 'e'];
v.drain(1..4); // dropped immediately; range is gone
assert_eq!(v, vec!['a', 'e']);

Reach for drain over retain when you’re removing a contiguous range by position (not a predicate), and over split_off when you don’t want a second allocation. If you need conditional removal from anywhere, that’s extract_if; if you just want the values gone, truncate is cheaper.

← Previous 238. slice::chunks_exact_mut — Edit a Slice in Fixed-Size Blocks Without Index Math Next → 240. to_le_bytes / from_le_bytes — Serialize Integers Without unsafe or Bit-Shifting