How can I update a value in a mutable HashMap?

56,131

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

Share:
56,131

Related videos on Youtube

Akavall
Author by

Akavall

I like programming, machine learning, statistics, all kinds of problem solving, and I play chess. My github

Updated on March 30, 2020

Comments

  • Akavall
    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 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?

  • Luke Dupin
    Luke Dupin over 6 years
    Is IndexMut expected to be implemented in the future?
  • Shepmaster
    Shepmaster over 6 years
    @LukeDupin doubtful. In its place the hypothetical IndexSet trait would be implemented.
  • Eftekhari
    Eftekhari about 5 years
    *my_map.entry("a").or_insert(42) += 10; Whad does that * do? Dereferencing for what part?
  • Shepmaster
    Shepmaster about 5 years
    @Eftekhari with added parenthesis: (*(my_map.entry("a").or_insert(42))) += 10;
  • Germán Faller
    Germán Faller over 4 years
    hello, 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
    Shepmaster over 4 years
    @GermanFaller let tmp = my_map.entry("a").or_insert(some_default); tmp.a += 1; tmp.b += 2.
  • Artyom Gevorgyan
    Artyom Gevorgyan almost 4 years
    Also, 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
    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.