Intermediate · Error handling and collections · topic 26 of 50 · 30 min
HashMap and HashSet
Explanation
Why a map?
Looking up a student's marks in a Vec of pairs means checking every pair until you find the name: slow for a big class. A hash map stores values by key, so lookup is about the same speed whether it holds 10 items or 10 million. A key is what you search by (a roll number, a city name); the value is what you get back.
use std::collections::HashMap;
fn main() {
let mut stock: HashMap<String, u32> = HashMap::new();
stock.insert(String::from("rice"), 40);
stock.insert(String::from("dal"), 25);
stock.insert(String::from("rice"), 35); // replaces 40
println!("{:?}", stock.get("rice")); // Some(35)
println!("{:?}", stock.get("ghee")); // None
println!("{}", stock.len()); // 2
}Hashing means turning a key into a number that tells the map where to store the value. You do not need the details; just know that keys must support hashing and equality (types like String, integers, char and bool do).
Ownership in maps
insert moves owned values like String into the map; the map now owns them. Copy values like u32 are copied. get gives back a reference to the value inside the map, so the map keeps ownership.
The entry API: insert or update
A very common job is "if the key exists, update it; otherwise insert a starting value". The entry API does this in one step. entry(key) looks up the key, or_insert(start) inserts the start value if missing, and both return a mutable reference to the value.
use std::collections::HashMap;
fn main() {
let text = "chai samosa chai jalebi chai samosa";
let mut counts: HashMap<&str, u32> = HashMap::new();
for word in text.split_whitespace() {
*counts.entry(word).or_insert(0) += 1;
}
println!("{:?}", counts.get("chai")); // Some(3)
}Looping and order
for (item, qty) in &stock {
println!("{item}: {qty}");
}Other useful methods
| Method | What it does |
|---|---|
| contains_key(&k) | Whether the key exists |
| remove(&k) | Remove and return Some(value), or None |
| get_mut(&k) | A mutable reference to change a value in place |
| keys() / values() | Iterate over just the keys or values |
| entry(k).or_insert_with(f) | Insert a value computed by f only when missing |
HashSet: unique items
A HashSet<T> is like a HashMap with keys only. It stores each value once and answers "is this in the set?" quickly. insert returns false if the value was already there, which is handy for spotting duplicates.
use std::collections::HashSet;
fn main() {
let mut seen = HashSet::new();
for phone in ["98", "97", "98", "96"] {
if !seen.insert(phone) {
println!("duplicate: {phone}"); // duplicate: 98
}
}
let a: HashSet<i32> = [1, 2, 3].into_iter().collect();
let b: HashSet<i32> = [2, 3, 4].into_iter().collect();
let common: HashSet<_> = a.intersection(&b).collect();
println!("{}", common.len()); // 2
}Common mistakes
- Forgetting use std::collections::HashMap;. The compiler error suggests the import.
- Expecting insertion order when printing. Use BTreeMap or sort the keys.
- Writing two steps (get, then insert) to update a count. Use entry(...).or_insert(...).
- Using f64 as a key. Floats cannot be hashed in std because NaN is not equal to itself; use integers (like paise) or strings.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
HashMap and HashSet · 1/6
A HashMap stores key value pairs and finds a value by its key very fast, however big the map is.
Prefer a video by a teacher? Find free videos on “rust hashmap entry api hashset tutorial” on YouTube ↗
In real life: Counting UPI payments by app
A small shop exports its day's payments: each line has the app used (GPay, PhonePe, Paytm...) and the amount. A HashMap<String, (u32, u64)> keyed by app name counts payments and sums amounts in one pass using the entry API. The owner then sees which app customers use most, without caring about the order the lines came in.
use std::collections::HashMap;
fn main() {
let payments = [("PhonePe", 120), ("GPay", 80), ("PhonePe", 45), ("Paytm", 300), ("GPay", 60)];
let mut by_app: HashMap<&str, (u32, u64)> = HashMap::new();
for (app, amount) in payments {
let entry = by_app.entry(app).or_insert((0, 0));
entry.0 += 1;
entry.1 += amount;
}
let mut apps: Vec<_> = by_app.into_iter().collect();
apps.sort(); // sorted by app name
for (app, (count, total)) in apps {
println!("{app}: {count} payments, Rs {total}");
}
}Practice (0/3 done)
What does this print? use std::collections::HashMap; let mut m = HashMap::new(); m.insert("a", 1); m.insert("a", 5); *m.entry("b").or_insert(0) += 2; println!("{:?} {:?}", m.get("a"), m.get("b"));
You need the keys printed in alphabetical order every time. Which type should you use?
Read a sentence and count how many times each word appears (ignore capital letters with to_lowercase()). Print the words sorted by count, highest first. Then use a HashSet to print the number of different words.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Custom error typesError handling and collections · same module
- Result and the ? operatorError handling and collections · same module
- Collections: List, Dictionary, HashSet, Queue and StackC# and .NET · Collections, generics and LINQ
- Maps: HashMap, LinkedHashMap and TreeMapJava Programming (Core to Spring Boot) · The collections framework
- std::map and std::unordered_mapC++ Programming (modern C++ and DSA) · The Standard Template Library (STL)
- Sets: HashSet, LinkedHashSet and TreeSetJava Programming (Core to Spring Boot) · The collections framework
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does map.get(&key) return?
Answer: An Option with a reference to the value
get returns Option<&V> because the key may be missing.
What does entry(k).or_insert(0) return?
Answer: A mutable reference to the value for k (inserting 0 if needed)
It returns &mut V, so you can update the value in place.
Which statement about HashMap order is true?
Answer: There is no guaranteed order
Iteration order of a HashMap is not specified.
What does HashSet::insert return when the value is already present?
Answer: false
insert returns false if the set already had the value.
Which key type cannot be used in a std HashMap?
Answer: f64
f64 does not implement Hash and Eq, because NaN is not equal to itself.
What does HashSet<T>.Add return for a value already in the set?Related
Answer: false
false means nothing was added.
From Collections: List, Dictionary, HashSet, Queue and Stack
What does map.get(key) return for a missing key in a HashMap?Related
Which call returns end() when the key is missing, without inserting?Related
What does set.add(x) return if x is already present?Related
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Error handling and collections”. The module exam comes next.
- Module 6 exam: Error handling and collections · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Modules, crates, traits and generics. 50 topics left in the course before the final exam.