How can I update a value in a mutable HashMap?
Solution 1
Indexing immutably and indexing mutably are provided by two different traits: Index
and IndexMut
, respectively.
Currently, HashMap
does not implement IndexMut
, while Vec
does.
The commit that removed HashMap
's IndexMut
implementation states:
This commit removes the IndexMut impls on HashMap and BTreeMap, in order to future-proof the API against the eventual inclusion of an IndexSet trait.
It's my understanding that a hypothetical IndexSet
trait would allow you to assign brand-new values to a HashMap
, and not just read or mutate existing entries:
let mut map = HashMap::new();
map["key"] = "value";
For now, you can use get_mut
:
*my_map.get_mut("a").unwrap() += 10;
Or the entry
API:
*my_map.entry("a").or_insert(42) += 10;
Solution 2
Considering:
let mut m = std::collections::HashMap::new();
m.insert("a", 1);
m.insert("b", 3);
let k = "c";
If the key already exists:
m.insert(k, 10 + m[k] );
If the key not exists:
- You may update a value of the key:
m.insert(k, 10 + if m.contains_key(k) { m[k] } else { 0 });
- Or first insert a key only if it doesn't already exist:
m.entry(k).or_insert(0);
m.insert(k, 200 + m[k]);
- Or update a key, guarding against the key possibly not being set:
*m.entry(k).or_insert(0) += 3000;
Finally print the value:
println!("{}", m[k]); // 3210
See:
https://doc.rust-lang.org/std/collections/struct.HashMap.html
Solution 3
I will share my own Answer because I had this issue but I was working with Structs so, that way in my case was a little bit tricky
use std::collections::HashMap;
#[derive(Debug)]
struct ExampleStruct {
pub field1: usize,
pub field2: f64,
}
fn main() {
let mut example_map = HashMap::new();
&example_map.insert(1usize, ExampleStruct { field1: 50, field2: 184.0});
&example_map.insert(6usize, ExampleStruct { field1: 60, field2: 486.0});
//First Try
(*example_map.get_mut(&1).unwrap()).field1 += 55; //50+55=105
(*example_map.get_mut(&6).unwrap()).field1 -= 25; //60-25=35
//Spliting lines
let op_elem = example_map.get_mut(&6);
let elem = op_elem.unwrap();
(*elem).field2 = 200.0;
let op_ok_elem = example_map.get_mut(&1);
let elem = op_ok_elem.unwrap_or_else(|| panic!("This msg should not appear"));
(*elem).field2 = 777.0;
println!("Map at this point: {:?}", example_map);
let op_err_elem = example_map.get_mut(&8);
let _elem = op_err_elem.unwrap_or_else(|| panic!("Be careful, check you key"));
println!("{:?}", example_map);
}
You can play with this on Rust Playground
Related videos on Youtube
![Akavall](https://i.stack.imgur.com/wApmM.jpg?s=256&g=1)
Akavall
I like programming, machine learning, statistics, all kinds of problem solving, and I play chess. My github
Updated on March 30, 2020Comments
-
Akavall over 4 years
Here is what I am trying to do:
use std::collections::HashMap; fn main() { let mut my_map = HashMap::new(); my_map.insert("a", 1); my_map.insert("b", 3); my_map["a"] += 10; // I expect my_map becomes {"b": 3, "a": 11} }
But this raises an error:
Rust 2015
error[E0594]: cannot assign to immutable indexed content --> src/main.rs:8:5 | 8 | my_map["a"] += 10; | ^^^^^^^^^^^^^^^^^ cannot borrow as mutable | = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::collections::HashMap<&str, i32>`
Rust 2018
error[E0594]: cannot assign to data in a `&` reference --> src/main.rs:8:5 | 8 | my_map["a"] += 10; | ^^^^^^^^^^^^^^^^^ cannot assign
I don't really understand what that means, since I made the
HashMap
mutable. When I try to update an element in avector
, I get the expected result:let mut my_vec = vec![1, 2, 3]; my_vec[0] += 10; println! {"{:?}", my_vec}; // [11, 2, 3]
What is different about
HashMap
that I am getting the above error? Is there a way to update a value? -
Luke Dupin over 6 yearsIs IndexMut expected to be implemented in the future?
-
Shepmaster over 6 years@LukeDupin doubtful. In its place the hypothetical
IndexSet
trait would be implemented. -
Eftekhari about 5 years
*my_map.entry("a").or_insert(42) += 10;
Whad does that * do? Dereferencing for what part? -
Shepmaster about 5 years@Eftekhari with added parenthesis:
(*(my_map.entry("a").or_insert(42))) += 10;
-
Germán Faller over 4 yearshello, How can I split the last line, imagine Im working with stucts and I want to change 2 values, I will not call get twice so...
(*example_map.get_mut(&1).unwrap()).field1+=55;
some one can explain this to me, Im dealing with this and I dont know what to write, if let. let mut, let &mut ... Thanks! -
Shepmaster over 4 years@GermanFaller
let tmp = my_map.entry("a").or_insert(some_default); tmp.a += 1; tmp.b += 2
. -
Artyom Gevorgyan almost 4 yearsAlso, if I understand correctly, if you just want to update the value of a certain key, you can simply insert the new pair
(key, map)
into a map and get what you want. -
Shepmaster almost 4 years@ArtemHevorhian perhaps, but that's not how I'd usually interpret "update". To me, update means "based on the previous value", while inserting again would be closer to replacing. Additionally for Rust, you may wish to mutate the value (e.g. add something to a
Vec
) instead of creating a whole new value.