Lesson 6.3 · 9 min
Lookup by key: HashMap
A Vec finds things by position. But "how many messages has ada sent?" is a question about a name, not a position. For lookups by name, Rust has the HashMap, and it follows the same owner rule as everything else.
Adds to ChatterboxA HashMap counts messages per author.
A table of keys and values
A HashMap is a table. Every row has a key (the thing you look up by) and a value (what you find). You write the type as HashMap<String, u32>: keys are Strings, values are numbers. It lives in the standard library, so a program that wants one starts with use std::collections::HashMap;.
The owner rule again: the map owns its keys and its values. When you insert a String key, it moves into the table.
$ cargo run
Some(3)
Noneget returns an Option, as it did on a Vec: Some(3) when the key is there, None when it is not. A name nobody has used is not an error, just an empty answer.
What if you insert the same key twice?
What does this program print?
Some(1)Some(2)None
Reveal the answer
The program prints: Some(2) A key appears once in a map. A second insert with the same key replaces the old value, so the table holds one row for "sam" and its value is 2.
Counting: the entry API
Counting messages per user needs three steps: find the user's row, make one starting at 0 if it does not exist, then add 1. Rust does all three with one line.
counts.entry(name) points at the row for that key, whether it exists yet or not. .or_insert(0) says "if there is no row, create it with 0", and hands back a borrow of the value that lives in the table. The * in front means "the number the borrow points to", so += 1 changes the number inside the map.
$ cargo run
sam said 3 things
ada said 1 thingsNo order
A HashMap does not remember the order you inserted rows, and the order it walks them in can change between runs. That is the price of fast lookup. If you want to print a map in a stable order, collect the keys into a Vec and sort it, which is what /stats does below.
Chatterbox v0.6: who talks most
Each person now has a name next to their line, so the loop walks pairs of (user, line). Every Say adds one to that user's count. A new /stats command prints each name and its count in alphabetical order. The history from the last lesson is untouched.
$ cargo run
ada: 1
sam: 2Write count_by_author. Walk the history and return a map from each author name to how many messages that author wrote.
Write count_by_author. Walk the history and return a map from each author name to how many messages that author wrote.
Hidden tests (3): empty_history_has_no_rows counts_each_author unknown_author_is_absent
This exercise runs real Rust in your browser through the official Rust Playground, so it needs JavaScript.
What you have now
Chatterbox answers two kinds of question: "what was said, in what order" (a Vec) and "how many has this person said" (a HashMap). Pick the container by the question you ask: by position, or by name. One more tool for this chapter: the tiny in-place functions you have been reading as |m| ….
Say it out loud, then reveal
What does counts.get("zoe") return when zoe was never inserted?Reveal
What does *counts.entry(name).or_insert(0) += 1 do?Reveal
Why does /stats sort the names before printing?Reveal
That is lesson 6.3. Tick it off so the course remembers where you are.
Questions people ask
What do I need to write to use HashMap in Rust?
Add use std::collections::HashMap; at the top of the file. It is in the standard library, not in the prelude.
What happens if I insert the same key twice?
The second value replaces the first. A map holds at most one value per key.
Is a Rust HashMap like a Python dict?
Yes for lookup by key. Differences: all keys share one type, all values share one type, and iteration order is not something you should rely on.