Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to borrow parts of a struct as mutable and other parts as immutable?

Is it possible to borrow parts of a struct as mutable, and another part as immutable - if the struct's fields are private.

fn main() {
    let mut ecs = EntityComponentSystem::new();

    for e_id in ecs.get_entities_with_component::<Velocity>().unwrap() {
           let components = ecs.get_mut_components(e_id);
           ...
}

impl EntityComponentSystem {
    ...
    pub fn get_entities_with_component<K: Key>(&self) -> Option<&HashSet<u64>> {
        self.entities_with_components.get(&TypeId::of::<K>())
    }

    pub fn get_mut_components(&mut self, entity_id: u64) -> &mut TypeMap {
        let entity = self.entities.get_mut(&entity_id).unwrap();
        &mut entity.components
    }
}

pub struct EntityComponentSystem {
    entities: HashMap<u64, Entity>,                     <------- I would like to modify this.
    entities_with_components: HashMap<TypeId, HashSet<u64>>,   <---- while reading from this!
}

The compiler is giving me:

error[E0502]: cannot borrow `*ecs` as mutable because it is also borrowed as immutable
  --> physics.rs:19:26
   |
18 |     for e_id in ecs.get_entities_with_component::<Velocity>() {
   |                 --- immutable borrow occurs here
19 |         let components = ecs.get_mut_components(*e_id);
   |                          ^^^ mutable borrow occurs here
...
26 |     }
   |     - immutable borrow ends here

What I'm not grasping, is how the &self reference in get_entities_with_component is still borrowed after we've essentially returned a part of the entities_with_components field.

Shouldn't just that part be borrowed? Is there any way to enforce this?

like image 713
Kim Avatar asked Aug 24 '17 10:08

Kim


2 Answers

You can only borrow the entire struct as immutable or mutable, there is no concept of borrowing only parts of it. When this becomes a problem, you can use interior mutability in the form of a RefCell:

pub struct EntityComponentSystem {
    entities: RefCell<HashMap<u64, Entity>>,
    entities_with_components: HashMap<TypeId, HashSet<u64>>,
}

Now you can borrow the entire struct as immutable and borrow the contents of the RefCell independently as mutable:

pub fn get_mut_components(&self, entity_id: u64) -> &mut TypeMap {
    let mut entities = self.entities.borrow_mut();
    let entity = entities.get_mut(&entity_id).unwrap();
    &mut entity.components
}
like image 154
Jan Hohenheim Avatar answered Oct 31 '22 08:10

Jan Hohenheim


No, a function cannot return a reference to a part of the structure and leave the structure partially borrowed.

However you can return tuple of immutable and mutable borrows like this

#[derive(Debug)]
struct AB(u32, u32);

impl AB {
    fn borrow_parts(&mut self) -> (&u32, &mut u32) {
        (&self.0, &mut self.1)
    }
}

fn main() {
    let mut ab = AB(0, 2);
    {
        let (a, b) = ab.borrow_parts();
        *b = *a;
    }
    println!("{:?}", ab);
}
like image 25
red75prime Avatar answered Oct 31 '22 10:10

red75prime