How can I update a value in a mutable HashMap?

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 a vector, 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?


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:

  1. You may update a value of the key:
    m.insert(k, 10 + if m.contains_key(k) { m[k] } else { 0 });
  1. Or first insert a key only if it doesn't already exist:
    m.entry(k).or_insert(0);
    m.insert(k, 200 + m[k]);
  1. 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