Access to another element unchanged when a HashMap element changes

I am working on a game that includes a bunch of Beetle objects stored in a HashMap . Each bug has a position, and it can also have a target identifier, which is the key for another bug in the hash. If the beetle has a target, it must move toward the target each time a game loop is executed.

I cannot search for the current current position because you cannot have volatile and immutable borrowing. I understand this, but any ideas how to restructure for my particular case?

I think I just caught how easy it would be in any other language, I can't see the idiomatic way of rust. Here's a pretty minimal but complete example:

 use std::collections::HashMap; type Beetles = HashMap<i32, Beetle>; struct Beetle { x: f32, y: f32, target_id: i32, } impl Beetle { fn new() -> Beetle { Beetle { x: 0.0, y: 0.0, target_id: -1, } } } fn main() { let mut beetles: Beetles = HashMap::new(); beetles.insert(0, Beetle::new()); beetles.insert(1, Beetle::new()); set_target(&mut beetles, 0, 1); move_toward_target(&mut beetles, 0); } fn set_target(beetles: &mut Beetles, subject_id: i32, target_id: i32) { if let Some(subject) = beetles.get_mut(&subject_id) { subject.target_id = target_id; } } fn move_toward_target(beetles: &mut Beetles, beetle_id: i32) { if let Some(subject) = beetles.get_mut(&beetle_id) { if let Some(target) = beetles.get(&subject.target_id) { // update subject position to move closer to target... } } } 
+5
source share
1 answer

You can solve your specific problem by doing a double object search. First, always borrow from a hash map to collect the information needed to update the item. Then, finally, update the theme using the collected information using hash map borrowing:

 fn move_toward_target(beetles: &mut Beetles, beetle_id: i32) { if let Some(subject_target_id) = beetles.get(&beetle_id).map(|b| b.target_id) { let mut target_xy = None; // example if let Some(target) = beetles.get(&subject_target_id) { // collect information about target relevant for updating subject target_xy = Some((target.x, target.y)) // example } let subject = beetles.get_mut(&beetle_id).unwrap(); // update subject using collected information about target if let Some((target_x, target_y)) = target_xy{ // example subject.x = target_x; subject.y = target_y; } } } 

However, most likely, you will encounter similar and more complex problems with your bugs in the future, because bugs are your central game objects, which you probably want to refer to variably and invariably at the same time at several places in your code. Therefore, it makes sense to wrap your bugs in std::cell::RefCell s, which check borrowing rules dynamically at runtime. This gives you more flexibility when linking to bugs in your hash map:

 fn main() { let mut beetles: Beetles = HashMap::new(); beetles.insert(0, RefCell::new(Beetle::new())); beetles.insert(1, RefCell::new(Beetle::new())); set_target(&mut beetles, 0, 1); move_toward_target(&mut beetles, 0); } fn set_target(beetles: &mut Beetles, subject_id: i32, target_id: i32) { if let Some(mut subject) = beetles.get_mut(&subject_id).map(|b| b.borrow_mut()) { subject.target_id = target_id; } } fn move_toward_target(beetles: &mut Beetles, beetle_id: i32) { if let Some(mut subject) = beetles.get(&beetle_id).map(|b| b.borrow_mut()) { if let Some(target) = beetles.get(&subject.target_id).map(|b| b.borrow()) { //example for updating subject based on target subject.x = target.x; subject.y = target.y; } } } 

Updated Beetles Type:

 type Beetles = HashMap<i32, RefCell<Beetle>>; 
+3
source

Source: https://habr.com/ru/post/1276197/


All Articles