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 anOption.- The map must be declared with
let mutbefore 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, andor_insertsupplies a default when it doesn't.- Updating an existing key never changes the map's length.
