How to get a sub collection of mutable elements

Hi all !

Is it possible to have an implementation of get_entities_mut in a safe and performant way?

use std::collections::HashMap;

pub struct Entity {
    pub id: i32,
}

pub struct EntityContainer {
    pub entities: HashMap<i32, Entity>,
}

impl EntityContainer {
    pub fn set_entity(&mut self, entity: Entity) {
        self.entities.insert(entity.id, entity);
    }

    pub fn get_entities<'a>(&'a self, ids: &[i32]) -> Vec<Option<&'a Entity>> {
        ids.iter().map(|id| self.entities.get(id)).collect()
    }

    pub fn get_entities_mut<'a>(&'a mut self, ids: &[i32]) -> Vec<Option<&'a mut Entity>> {
        let mut entities = Vec::with_capacity(ids.len());
        for id in ids {
            entities.push(self.entities.get_mut(id));
        }
        entities
    }
}

Rust Playground

My current implementation cannot work because is self.entities is borrowed more than once:

   Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `self.entities` as mutable more than once at a time
  --> src/lib.rs:23:27
   |
20 |     pub fn get_entities_mut<'a>(&'a mut self, ids: &[i32]) -> Vec<Option<&'a mut Entity>> {
   |                             -- lifetime `'a` defined here
...
23 |             entities.push(self.entities.get_mut(id));
   |                           ^^^^^^^^^^^^^ `self.entities` was mutably borrowed here in the previous iteration of the loop
24 |         }
25 |         entities
   |         -------- returning this value requires that `self.entities` is borrowed for `'a`

For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` (lib) due to 1 previous error

Thank you for your help!

There this unstable method (which also requires a known length).

Otherwise I think you have to use iteration or create a HashMap<i32, &mut Entity> and use remove or similar.

4 Likes

Hi @quinedot,

Could you give hint for these suggestions, please?

Thank you!

The HashMap suggestion:

    pub fn get_entities_mut<'a>(&'a mut self, ids: &[i32]) -> Vec<Option<&'a mut Entity>> {
        // This is the `HashMap<i32, &mut Entity>`
        let mut tmp: HashMap<_, _> = self
            .entities
            .iter_mut()
            .map(|(&k, v)| (k, v))
            .collect();

        ids.iter().map(|k| tmp.remove(k)).collect()
    }

The iteration suggestion was a poor one for this use case.

3 Likes