Skip to content

Vec::drain

Vec methods · Collections

Level: reference · for working programmers

One line: Remove a range and yield the removed elements as an iterator.

pub fn drain<R>(&mut self, range: R) -> Drain<'_, T, A>
where
    R: RangeBounds<usize>,

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 instead
  • Vec::extract_if — remove by predicate rather than by range, and lazily
  • Vec::splice — drain, and put something else in its place
  • Vec::into_iter — the consuming version
  • Vec::append — moving everything into another vector, in one bulk copy rather than through an iterator

Vec::drain in the standard library ↗