#Borrow checker and storing mutable references to components

1 messages · Page 1 of 1 (latest)

native brook
#

I have a rather tricky situation where the borrow checker is getting in the way of me doing something I need to do. I won't go into the specifics, but the user can either select the left or right part of a 'checkpoint'. The left part stores the data which can be modified, and the right part stores the entity ID of the left part. The way I have implemented my UI system is that the UI functions take in an iterator over mutable references to components, so that if multiple are selected they can all be modified, and I don't want to change this.
For editing the checkpoint, I want the user to be able to select the right part of it, and the data to still be modifiable from the left part. So, I need to go through each right part which is selected, get the left entity, get a mutable reference to that transform, and store it. But here's the problem: I also need to store mutable references to any left parts which are selected and store them in the same place. Here is what I currently have:

    q_cp_left: Query<'w, 's, (&'static mut CheckpointLeft, Entity, Has<Selected>)>,
    q_cp_right: Query<'w, 's, &'static CheckpointRight, With<Selected>>,
        let mut cps: HashMap<Entity, Mut<CheckpointLeft>> = HashMap::new();
        for (cp_l, e, selected) in self.q_cp_left.iter_mut() {
            if selected {
                cps.insert(e, cp_l);
            }
        }
        for cp_r in self.q_cp_right.iter() {
            let Ok((cp_l, e, _)) = self.q_cp_left.get_mut(cp_r.left) else {
                continue;
            };
            cps.insert(e, cp_l);
        }

And the borrow checker isn't having any of it, and rightly so - I'm mutably borrowing q_cp_left twice.
But I know that there won't ever be 2 mutable references to the same data at the same time, because I'm storing it in a hashmap so the same entity's transform can't be added twice.
How can I get around this problem? Thanks!

lean pasture
#

First of all: Hey, I'm not the only one who prefixes my queries with q_!

Second of all, I would just collect the cp_r.left and use it as a criteria in the first loop. Basically, gather the information you need first and then collect the mutable references.

let cp_left_of_right: EntitySet = q_cp_right.iter().map(|cp_r|cp_r.left).collect();

let mut cps: EntityMap<Mut<CheckpointLeft>> = EntityMap::new();

for (cp_l, e, selected) in q_cp_left.iter_mut() {
   if selected || cp_left_of_right.contains(e) {
      cps.insert(e, cp_l)
   }
}

(Use of EntitySet or EntityMap over the typical hash variants optional)

native brook
#

And yeah haha I can’t remember where I first saw the q_ convention, but I really liked it as it’s much more concise but still makes it clear it’s a query and not the data itself.

#

I do the same with events, I put ev_ before them