Filtering a struct field Vec containing Box instances

I have a Vec of Box of a trait, and I want to be able to filter it on some predicate, mutating the vec (which is a field of a long lived struct).

struct Environment {
    entities: Vec<Box<dyn Entity>> 
}
impl Environment {
    fn prune(&mut self) {
        self.entities = self.entities.into_iter().filter(|e| e.is_alive()).collect();
    }
}

And here's the specific error I get.

self.entities = self.entities.into_iter().filter(|e| e.is_alive()).collect();
                ^^^^^^^^^^^^^ move occurs because `self.entities` has type `Vec<Box<dyn Entity>>`, which does not implement the `Copy` trait

Here's a link to a playground showing a minimal example: Rust Playground

Anyway, the error makes sense! But I don't know how to resolve it. I feel like I would want one of a couple options:

  • Use some kind of variant on filter which mutates the vec in place
  • Transfer ownership from the old self.entities into the new self.entities after the filter operation.

You want Vec::retain():

    self.entities.retain(|e| e.is_alive());

Another approach is to temporarily replace self.entities, do your operation, and then replace self.entities again. Something like (untested):

    let tmp = std::mem::replace(&mut self.entities, Vec::new());
    tmp.into_iter().filter(|e| e.is_alive()).collect();
    self.entities = tmp;
2 Likes

You can also shorten that a bit by using take (new back in 1.40.0):

let tmp = std::mem::take(&mut self.entities);

(Since Vec<T>: Default. And constructing an empty Vec is essentially free -- no memory allocation required -- so the replace/take approach is still highly efficient.)

1 Like

Vec::retain, holy smokes that's precisely what I was hoping for. Thanks!