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:
| |
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
| |
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:
| |
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.
| |
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.