Borrowing two values from the same HashMap

Trying to figure out how to solve borrow problems like these:

fn update(&mut self, component_map: &mut HashMap<ComponentType, HashMap<u32, Box<dyn Component>>>, dt: f32) {
	let move_components = component_map.get(&ComponentType::Movement).unwrap();
	let transform_components = component_map.get_mut(&ComponentType::Transform).unwrap();
		
	for (id, component) in move_components.iter(){
		transform_components.get_mut(id).unwrap().to_transform_mut().unwrap().position += dt*component.to_movement().unwrap().velocity;
		}
}

So I want to mutate the transform component for every id based on data in the movement component but rust is not letting me even fetch both as I'm getting both a mutable and immutable reference from the component_map. Rust of course don't know that this isn't a problem since I'm fetching two different component HashMaps even though I'm fetching them from the same HashMap.

This feels like it should be rust 101 how to fix these types of borrow situations, but I'm really struggling to figure out how. I guess I could first loop over all the ids in the movement components, store them in a Vec and them loop over that vec, fetching exactly one movement component at a time, copying the velocity value and then fetching exactly one transform component and mutating that but then I'm following a ton of pointers and doing a lot of unnecessary computation that I should be able to avoid :thinking:. Any tips, especially of how this type of problem would be solved in idiomatic rust would be helpful!

The recommended solution is probably RefCell, which tracks borrows at runtime instead of compile time, and can work with these cases better.

1 Like

There are a few options:

  1. Use shared mutability (e.g. RefCell, Mutex, RwLock) in the elements of the map, so you do not need a mutable/exclusive borrow of the HashMap itself.

  2. Remove the item(s) from the HashMap before using them, and put them back afterward.

  3. Provide a safe API that uses unsafe and raw pointers internally to return mutable references to multiple items, after checking that they are all distinct.

Method 1 is probably the most straightforward. It does add some run-time overhead, though I think in this case it could be quite small, since it wouldn't impact the inner loop. Here's an example:

fn update(component_map: &HashMap<ComponentType, RefCell<HashMap<u32, Box<dyn Component>>>>, dt: f32) {
	let move_components = component_map[&ComponentType::Movement].borrow();
	let mut transform_components = component_map[&ComponentType::Transform].borrow_mut();
		
	for (id, component) in move_components.iter() {
		let transform = transform_components.get_mut(id).unwrap();
	}
}

Playground

Method 2 is fairly similar to 1: It again adds a bit of syntactical and run-time overhead for getting at the inner maps.

Method 3 could be very tricky to implement correctly, since HashMap does not expose any methods that operate directly on raw pointers, and there are lots of subtle ways to trigger undefined behavior when going back and forth between references and raw pointers. However, I think many of the published ECS crates end up using some unsafe code, presumably for maximum performance.

3 Likes

My code uses the second option -- remove, use, insert -- which I found it to be effective and easy to understand. (Well, it was easy to understand once I put in a comment explaining what
the remove was for.) The only problem I ran into was an unfortunate edit that that skipped the final insert.

1 Like

Thanks guys!

Ah, it didn't occur to me that removing it from the hashmap solves the issue, clever.

Re: the unsafe bit, yeah, I imagined that would be how people would do it, simply for performance reasons. Trying to learn rust though so kind of wanted to understand how elegantly to do it within safe mode (before resorting to brute force C-mode ;)). Great rundown of options, I really appreciate it!