What a HashMap is
A dictionary stores key-value pairs: you look up a value by its key rather than by a position. Rust's dictionary type is HashMap. Typical uses are scores by student name, counts by word, or prices by product code, anywhere you'd otherwise search a list for a matching entry.
HashMap is not in the prelude, so it has to be imported before it can be used:
use std::collections::HashMap;
Without that line, HashMap::new() fails with failed to resolve: use of undeclared type `HashMap`, and the compiler's help suggests the import. The exercise below already has it in place.
Creating a map
Start with an empty map and add entries with insert(key, value). The key comes first and the value second:
use std::collections::HashMap;
let mut scores: HashMap<&str, i32> = HashMap::new();
scores.insert("Alice", 95);
scores.insert("Bob", 87);
scores.insert("Charlie", 72);
The type HashMap<&str, i32> says that keys are string slices and values are 32-bit integers. You can also leave the annotation out. The compiler infers both types from the first insert call, so let mut scores = HashMap::new(); is enough.
When you already know the entries, build the map in one expression from an array of pairs:
let ages: HashMap<&str, i32> = HashMap::from([
("Alice", 25),
("Bob", 30),
]);
println!("{} {}", ages["Alice"], ages.len());
25 2
The method len() returns the number of entries, just as it does for a vector.
Rules and gotchas
The map must be mut to change it
Both insert and remove change the map, so the binding needs let mut. Without it, the compiler says it cannot borrow the map as mutable. A map built once with HashMap::from and never changed can be an ordinary let.
One type for keys, one for values
All keys share one type and all values share another. Mixing them, for example inserting a decimal number into a map of integers, is a "mismatched types" compile error.
Keys are unique
Inserting a key that is already there replaces its value and returns the old one wrapped in Some. A brand-new key returns None.
let mut m: HashMap<String, i32> = HashMap::new();
println!("{:?}", m.insert("a".to_string(), 1));
println!("{:?}", m.insert("a".to_string(), 2));
println!("{:?}", m);
None
Some(1)
{"a": 2}
Checking and removing
The method contains_key tells you whether a key is present, and remove deletes an entry and returns its old value as an Option. With the three scores above, scores.contains_key("Bob") is true, and scores.remove("Bob") returns Some(87) and leaves two entries.
No guaranteed order
A HashMap doesn't remember insertion order, and the order in which it lists its entries can differ from one run to the next. Don't rely on it. If you need sorted output, collect the entries into a vector and sort that.
The lesson's exercise
The task is to create a map named scores with three students.
- Create an empty map. Declare
scoreswithlet mutandHashMap::new(). It must bemutbecause the next step adds entries. No type annotation is needed, since the inserts that follow tell the compiler the key and value types. - Insert three entries. One
insertcall per student:"Alice"with 95,"Bob"with 87 and"Charlie"with 72.
let mut scores = HashMap::new();
scores.insert("Alice", 95);
scores.insert("Bob", 87);
scores.insert("Charlie", 72);
Afterwards scores.len() is 3.
Key takeaways
- A
HashMap<K, V>maps keys of typeKto values of typeV; import it withuse std::collections::HashMap;. - Create it with
HashMap::new()andinsert(key, value), or in one step withHashMap::from([...]). - Declare it
mutif you will add or remove entries. - Inserting an existing key replaces the value; the iteration order is not defined.
