Vec::drain¶
Level: reference · for working programmers
One line: Remove a range and yield the removed elements as an iterator.
Stable since 1.6.0.
The elements are moved out, so T need not be Clone, and the vector keeps its buffer — that is the difference from into_iter, which consumes the vector itself. drain(..) empties a vector you intend to keep using.
It removes the range whether or not you consume the iterator. Dropping the Drain without collecting still takes the elements out; the removal is the method's job, not the iterator's. That surprises people who expect laziness — and it is the opposite of extract_if, which really is lazy.
The iterator is double-ended, so .rev() works, and it is a normal iterator otherwise: .filter(), .map(), .collect().
Panics if the range is out of bounds or inverted.
Three ways to empty a vector, and they differ in what survives:
| call | elements | the vector | the buffer |
|---|---|---|---|
clear |
dropped | kept | kept |
drain(..) |
handed to you | kept | kept |
into_iter |
handed to you | consumed | consumed |
Example¶
vec_drain.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.
fn main() {
// Removes a range and hands you the removed elements as an iterator.
let mut v = vec![1, 2, 3, 4, 5];
let taken: Vec<i32> = v.drain(1..3).collect();
println!("taken {taken:?} left {v:?}");
// A full-range drain empties the vector but keeps the buffer — unlike
// into_iter(), which consumes the vector itself.
let mut v = vec![1, 2, 3];
let cap = v.capacity();
let all: Vec<i32> = v.drain(..).collect();
println!("all {all:?} v {v:?} buffer kept {}", v.capacity() == cap);
// The elements are moved out, so non-Clone types work.
let mut owners = vec![String::from("Ada"), String::from("Ben")];
let first: Vec<String> = owners.drain(..1).collect();
println!("{first:?} then {owners:?}");
// Dropping the Drain without collecting still removes the range.
let mut v = vec![1, 2, 3, 4];
v.drain(1..3);
println!("dropped the iterator, range still gone: {v:?}");
// It is double-ended and lazy in the usual way.
let mut v = vec![1, 2, 3, 4, 5, 6];
let back: Vec<i32> = v.drain(..).rev().take(2).collect();
println!("last two, back to front: {back:?} v {v:?}");
// Compare the three ways of emptying a Vec:
// drain(..) elements out, vector and buffer stay
// clear() elements dropped, buffer stays
// into_iter() elements out, the vector is consumed
let mut a = vec![1, 2];
let out: Vec<i32> = a.drain(..).collect();
println!("drain -> {out:?}, a is still usable: {a:?}");
let hook = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let caught = std::panic::catch_unwind(|| { let mut v = vec![1, 2]; v.drain(0..9); });
std::panic::set_hook(hook);
println!("drain past the end panicked: {}", caught.is_err());
}
Verified output of vec_drain.rs — regenerated by tools/run_examples.py, never hand-typed.
taken [2, 3] left [1, 4, 5]
all [1, 2, 3] v [] buffer kept true
["Ada"] then ["Ben"]
dropped the iterator, range still gone: [1, 4]
last two, back to front: [6, 5] v []
drain -> [1, 2], a is still usable: []
drain past the end panicked: true
See also¶
Vec::clear— same removal, elements dropped insteadVec::extract_if— remove by predicate rather than by range, and lazilyVec::splice— drain, and put something else in its placeVec::into_iter— the consuming versionVec::append— moving everything into another vector, in one bulk copy rather than through an iterator