Start learning free

LESSON 112 OF 414 · DICTIONARIES

Updating Dictionary Values in Rust: insert and the Entry API

Lesson Read the full explanation

In Rust, you update a value in a HashMap by calling insert() with an existing key:

use std::collections::HashMap;
let mut scores: HashMap<&str, i32> = HashMap::new();
scores.insert("Alice", 95);
scores.insert("Bob", 87);

scores.insert("Alice", 99);     // update: Alice is now 99
scores.insert("Charlie", 72);   // add new entry

The insert() method both creates new entries and updates existing ones. You can also use the entry API to update only existing keys:

scores.entry("Alice").and_modify(|v| *v = 99);  // updates Alice if exists

Assignment
Follow the Coding Tutorial and update Alice's score to 99!


Hint
Use scores.insert("Alice", 99).

STEP 1 OF 1
    Ctrl + Enter to check

    RUST · DICTIONARIES

    Updating Dictionary Values in Rust, explained

    What updating a HashMap means

    A HashMap stores key-value pairs, and each key appears at most once. When you want a key to hold a different value, you don't edit the old pair. You hand the map the key together with the new value, and the map replaces what it had.

    The method for that is insert. It does two jobs with one call: if the key is missing, it adds a new entry, and if the key is already there, it overwrites the value.

    use std::collections::HashMap;
    
    fn main() {
        let mut scores = HashMap::from([("Alice", 95), ("Bob", 87)]);
    
        scores.insert("Alice", 99);     // Alice already exists: her value is replaced
        scores.insert("Charlie", 72);   // Charlie is new: a new entry is added
    
        println!("{}", scores["Alice"]);
        println!("{}", scores.len());
    }

    This prints:

    99
    3
    

    The map started with two entries. Replacing Alice's score didn't change the count, and adding Charlie raised it to three.

    What insert returns

    Every call to insert returns an Option holding the previous value. When the key was new there was no previous value, so you get None. When the key existed, you get Some(old_value).

    let old = scores.insert("Alice", 99);
    println!("{:?}", old);
    
    let old2 = scores.insert("Charlie", 72);
    println!("{:?}", old2);

    With the map above, the first call prints Some(95) and the second prints None. Most of the time you ignore the returned value, as the lesson does. It is useful when you need to know whether you just overwrote something.

    Why square brackets don't work

    In many languages you would write scores["Alice"] = 99. In Rust you can read a value with scores["Alice"], but you can't assign through the brackets. The compiler rejects it with this error:

    error[E0594]: cannot assign to data in an index of `HashMap<&str, i32>`
      = help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `HashMap<&str, i32>`

    The map has no way to hand out a writable slot for a key through the brackets, because the key might not exist yet. So updates go through methods: insert, get_mut or the entry API.

    The entry API: updating only what exists

    Sometimes you want to change a value only if its key is already present, and leave the map alone otherwise. A plain insert can't do that, because it would create the key. The entry API can:

    scores.entry("Alice").and_modify(|v| *v = 99);  // changes Alice only if she exists

    entry("Alice") looks the key up and gives back an Entry. and_modify takes a closure that receives a mutable reference to the value, which is why the closure writes *v: the star reaches through the reference to the number itself. If the key is absent, the closure never runs and nothing is added. With the map above, calling scores.entry("Dana").and_modify(|v| *v = 1) leaves the length at 3 and scores.get("Dana") still gives None.

    The same style is handy for changing a value based on its old one:

    scores.entry("Alice").and_modify(|v| *v += 1);
    *scores.entry("Dana").or_insert(0) += 5;

    The first line adds one to Alice's score if she exists. The second uses or_insert(0), which inserts 0 when the key is missing and then returns a mutable reference to the value either way, so += 5 works whether Dana was there or not. After these lines, Alice holds 100 and Dana holds 5.

    There is also get_mut, which returns an Option with a mutable reference:

    if let Some(v) = scores.get_mut("Bob") {
        *v -= 7;
    }

    Bob's score goes from 87 to 80. If "Bob" were missing, the if let body would simply be skipped.

    The exercise

    The editor starts with a map already built:

    let mut scores = HashMap::from([("Alice", 95), ("Bob", 87)]);

    The task is to change Alice's score to 99. There is one step: call scores.insert("Alice", 99) below the map. The key exists, so the value is replaced.

    The map is declared with let mut. Without mut, the compiler refuses every call that changes the map, including insert. The reference solution is:

        let mut scores = HashMap::from([("Alice", 95), ("Bob", 87)]);
    
        scores.insert("Alice", 99);

    Key takeaways

    • insert(key, value) adds a new entry or replaces the value of an existing key, and returns the old value as an Option.
    • The map must be declared with let mut before you can change it.
    • You can read with map[key], but you can't assign with it.
    • entry(key).and_modify(...) changes a value only when the key exists, and or_insert supplies a default when it doesn't.
    • Updating an existing key never changes the map's length.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free