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.