shaky.sh

How to update a map in rust

I'm learning rust, and it's been pretty tough to get used to the borrowing model.

Here's a scenario that baffled me:

use std::collections::HashMap;

fn main() {
let mut my_map = HashMap::from([("one", 1)]);

let val = my_map.get("one").unwrap();
my_map.insert("one", val + 1);
}

This feels like a straightforward operation: updating a value in a map. But the compiler complains about this:

use std::collections::HashMap;

fn main() {
let mut my_map = HashMap::from([("one", 1)]);

let val = my_map.get("one").unwrap();
// ----------------- immutable borrow occurs here
my_map.insert("one", val + 1);
// ^^^^^^^^^^^^^^^^^^^^^---^^^^^
// | |
// | immutable borrow later used here
// mutable borrow occurs here
}

Updating a value in a map is something I do all the time in JavaScript, why is it so hard in rust?

The problem is that I'm using val, which is a reference to a value in my_map, in the same statement that updates my_map. As I understand it, my_map might need to move or allocate more memory as part of the insert operation, and in the process of that, the val reference might become (essentially) a null pointer, which rust prevents.

The solution is simple:

let val = my_map.get("one").unwrap();
let new_val = val + 1;
my_map.insert("one", new_val);

Just allocate a new variable with the new value first! Then, when I'm doing the insert operation, I'm not using a reference to my_map memory at all.

There's another way to do this that's mind-bending for me:

let val = my_map.get_mut("one").unwrap();
*val = *val + 1;

We get a mutable reference to our map value, and then we can assign directly to that reference! I imagine this makes a ton of sense if you're coming from C, or another language with pointers or references. But for this JavaScript guy, that feels very weird.