Two ways to read a value
Once a HashMap holds entries, you read a value by its key. Rust offers two ways, and they differ in what they do when the key is missing.
Square brackets
Indexing with the key gives you the value directly:
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert("Alice", 95);
scores.insert("Bob", 87);
let alice_score = scores["Alice"];
let bob_score = scores["Bob"];
println!("{} {}", alice_score, bob_score);
95 87
The result is a plain number, which you can use in arithmetic or comparisons straight away. The price is a risk: if the key isn't in the map, the program panics with the message no entry found for key.
The get method
The method get returns an Option instead. It is Some(&value), a reference to the stored value, when the key exists, and None when it doesn't:
println!("{:?} {:?}", scores.get("Alice"), scores.get("Zed"));
Some(95) None
Because the result is an Option, the compiler makes you handle the missing case before you can use the number. Assigning scores.get("Alice") to an i32 variable is a "mismatched types" error: it expected i32 and found Option<&{integer}>.
Handling a missing key
Choose the form that fits what you know about the key.
- The key must exist. Indexing is fine, and a panic would signal a bug in the program.
- The key may be missing and you need a default. Copy the value out of the reference and fall back:
scores.get("Zed").copied().unwrap_or(0)gives0. - The key may be missing and you want to act only if it is there. Use
if let:
if let Some(s) = scores.get("Bob") {
println!("Bob {}", s);
}
This prints Bob 87, and does nothing for a name that isn't in the map.
You can also test first with contains_key, which returns a bool:
if scores.contains_key("Alice") {
println!("{}", scores["Alice"]);
}
This works but looks the key up twice. A single get with if let is usually cleaner.
Gotchas in Rust
Bracket indexing can only read
The bracket syntax is for reading. You can't create or change an entry with scores["Dana"] = 60. To add or replace, call insert; to update a value in place, there is entry, for example *scores.entry("Bob").or_insert(0) += 1, which adds the key with 0 first if it is missing.
Keys are looked up by content
A map with &str keys is read with a string literal, as above. A map whose keys are owned String values can be read with a &String or with a literal: both scores[&k] and scores["Alice"] work. Either way, the lookup compares the text, not the memory location.
Case matters
The key "alice" is a different key from "Alice". A lookup with the wrong capitalization is a missing key, so it panics with brackets and gives None with get.
The lesson's exercise
The starter code already builds scores from an array of pairs. The only step is to read Alice's score. Declare alice_score with let and assign it scores["Alice"]. The key exists, so indexing is safe, and the variable receives the plain number 95.
let scores = HashMap::from([("Alice", 95), ("Bob", 87), ("Charlie", 72)]);
let alice_score = scores["Alice"];
If you wrote scores.get("Alice") instead, alice_score would hold Some(95), an Option, and you would have to unwrap it before using the number.
Key takeaways
map[key]returns the value itself and panics if the key is missing.map.get(key)returnsSome(&value)orNone, so a missing key is handled instead of fatal.- Use
if let,copied().unwrap_or(default)orcontains_keywhen the key may be absent. - Reading uses brackets; adding or updating uses
insertorentry.
