Why check first
Reading a key that isn't in a HashMap with square brackets panics and stops the program. With capitals holding France and Japan, capitals["Spain"] ends with the message "no entry found for key". So when a key may be missing, ask first.
The method contains_key answers that question. It returns true when the key is in the map and false when it isn't:
let capitals = HashMap::from([("France", "Paris"), ("Japan", "Tokyo")]);
if capitals.contains_key("Japan") {
println!("{}", capitals["Japan"]); // Tokyo
}
if !capitals.contains_key("Spain") {
println!("No Spain"); // No Spain
}
Inside the first if, capitals["Japan"] can't panic, because the check just proved the key is there. Put ! in front for the opposite: !capitals.contains_key("Spain") is true here. (In a file of your own, a HashMap needs use std::collections::HashMap; at the top.)
A value is not a key
contains_key looks only at the keys. In capitals, contains_key("Paris") is false, because "Paris" is a value, not a key. Matching is exact, including upper and lower case, so contains_key("japan") is false too.
String keys in variables
When the map's keys are String values and the key you look for is in a String variable called name, pass a reference: contains_key(&name). The method only borrows the key to compare it, so name is still usable afterwards. A literal like "Japan" can be passed directly, because a map with &str keys is searched by &str, and even a map with String keys accepts a &str for lookups.
The task, step by step
The map pets goes from each pet's name to its kind: Rex is a dog, Tom a cat, Polly a parrot. The first step checks pets.contains_key("Tom") and prints Tom's kind with pets["Tom"] inside the if. The second step checks that "cat" is not a key. It is a value, Tom's kind, so !pets.contains_key("cat") is true and the message prints.
let pets = HashMap::from([("Rex", "dog"), ("Tom", "cat"), ("Polly", "parrot")]);
if pets.contains_key("Tom") {
println!("Tom is a {}", pets["Tom"]);
}
if !pets.contains_key("cat") {
println!("cat is not a key");
}
It prints:
Tom is a cat
cat is not a key
Another way: get
When you want to use the value as well as know the key exists, get does both in one step, so you don't search the map twice. if let Some(c) = capitals.get("Japan") { println!("{}", c); } prints Tokyo, and for "Spain" the if let body is skipped. A match with a None arm can print "unknown" for the missing key. For this lesson, though, the point is the plain yes-or-no question, and contains_key is the clearest way to ask it. The next lessons build on get.
Keys and values side by side
It helps to picture the map as two columns. The left column holds the names Rex, Tom and Polly, which are the keys. The right column holds dog, cat and parrot, which are the values. Every question you ask with contains_key is about the left column only. To ask about the right column you would have to look through the values, which is a different operation. In the task, the word "cat" lives in the right column, so as a key it is missing, and that is exactly what the second check proves.
Common mistakes
- Reading with
[]without checking, which panics for a missing key. - Checking a value instead of a key, as with
contains_key("cat")here. - Mismatched capitalisation between the key you wrote and the one stored.
Key takeaways
map.contains_key(key)returnstrueorfalseand looks only at keys.- Check before using
[], since a missing key panics. - For a
Stringvariable, pass&name.
