Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to drain parts of a vector based on a predicate?

Tags:

I'm trying to remove some elements from a vector, based on a predicate, and collecting the result. Here's a (not working) example with an expected result:

let mut v: Vec<i32> = vec![1, 2, 3, 4, 5, 6];  let drained: Vec<i32> = v.iter().filter(|e| (*e) % 2 == 0).drain(..).collect();  assert_eq!(v, vec![1, 3, 5]); assert_eq!(drained, vec![2, 4, 6]); 

This results in the error

error[E0599]: no method named `drain` found for type `std::iter::Filter<std::slice::Iter<'_, i32>, [closure@src/main.rs:4:45: 4:62]>` in the current scope  --> src/main.rs:4:64   | 4 |     let drained: Vec<i32> = v.iter().filter(|e| (*e) % 2 == 0).drain(..).collect();   |                                                                ^^^^^ 

There are several alternatives I looked at, none of them seem to be doing what I want:

  • Vec::retain removes the elements from the vector, but doesn't give back ownership of the removed elements.

  • v.drain(..).filter(condition).collect() returns the correct value for drained but empties the whole vector.

like image 673
WorldSEnder Avatar asked Oct 09 '17 16:10

WorldSEnder


1 Answers

Not in stable Rust 1.33.0. There's an unstable nightly feature called drain_filter that does exactly what you want:

#![feature(drain_filter)]  fn main() {     let mut v: Vec<i32> = vec![1, 2, 3, 4, 5, 6];      let drained: Vec<i32> = v.drain_filter(|&mut e| e % 2 == 0).collect();      assert_eq!(v, vec![1, 3, 5]);     assert_eq!(drained, vec![2, 4, 6]); } 

As a stable workaround, you may be able to use Iterator::partition, but it does not reuse the memory:

fn main() {     let v: Vec<i32> = vec![1, 2, 3, 4, 5, 6];      let (drained, v): (Vec<_>, Vec<_>) = v.into_iter().partition(|&e| e % 2 == 0);      assert_eq!(v, vec![1, 3, 5]);     assert_eq!(drained, vec![2, 4, 6]); } 
like image 70
Shepmaster Avatar answered Sep 22 '22 20:09

Shepmaster