When a missing key means none
Often a missing key just means "none yet". A shop whose stock map has no plums has 0 plums. But stock["plums"] can't express that: indexing a missing key panics. The tool for this is the get method.
get doesn't return the value directly. It returns an Option: Some(&5) when the key exists (a reference to the value inside the map), and None when it doesn't. For a map with only apples, stock.get("apples") prints as Some(5) and stock.get("plums") as None.
let stock = HashMap::from([("apples", 5)]);
println!("{}", *stock.get("apples").unwrap_or(&0)); // 5
println!("{}", *stock.get("plums").unwrap_or(&0)); // 0
Reading the one-liner
Three small pieces make up *stock.get("plums").unwrap_or(&0):
get("plums")gives theOption.unwrap_or(&0)takes the reference out of aSome, or uses a reference to 0 when the option isNone. It must be&0and not0, because it has to be the same type as whatgethands back: a reference.- The
*in front reads the number behind the reference, turning a&i32into a plaini32.
Without the *, assigning the result to a variable declared as i32 is a compile error: the compiler reports "expected i32, found &{integer}" and suggests dereferencing the borrow.
Reading this way doesn't add anything to the map. After both lookups above, stock.len() is still 1. With a key in a String variable, pass a reference to get, as in get(&name).
The task, step by step
The starter code already reads the pears with stock["pears"], which is safe because pears are in the map. There are no plums, so the first step looks up "plums" with get, applies unwrap_or(&0), puts * in front, and stores the number in plums. The second step prints a line like the pears line.
let stock = HashMap::from([("apples", 5), ("pears", 2)]);
let pears = stock["pears"];
println!("We have {} pears", pears);
let plums = *stock.get("plums").unwrap_or(&0);
println!("We have {} plums", plums);
It prints:
We have 2 pears
We have 0 plums
Why an Option
Rust has no null value. When a lookup might find nothing, the result type says so: Option forces you to decide what happens for None before you can use the number. unwrap_or is the shortest decision, "use this default". You could also write match or if let to run different code for the two cases, which is useful when the default isn't a plain number. For counting, though, 0 is exactly right: a missing key means none so far, so the default and the real value agree.
Following the lookups
In the task, the map has apples (5) and pears (2). The pears line works with plain indexing because the key is certainly there. For plums, get("plums") is None, unwrap_or(&0) turns it into a reference to 0, and * reads that 0. The final print then fills the placeholder in We have {} plums with 0. Pears would have worked with get too, and in real programs you would use get whenever the key is not guaranteed.
Common mistakes
- Using
[]for a key that might be missing. It panics instead of giving a default. - Writing
unwrap_or(0)without the&, which is a type mismatch. - Forgetting the
*, which leaves a reference where a number is expected. - Using plain
unwrap(), which panics onNonejust like indexing.
Key takeaways
map.get(key)returns anOption:Somefor a present key,Nonefor a missing one.*map.get(key).unwrap_or(&0)is the value, or 0 for a missing key.- Reading with
getnever adds a key to the map.
