Problem overview
The function count_vote receives votes, a HashMap from names to how many votes each person has, and the name on a new vote. It gives that person one more vote and returns the map. A name with no votes yet isn't in the map: it starts from 0, so after this vote it has 1. The order of the entries doesn't matter.
With {"Ana": 3, "Ben": 5} and "Ana", Ana goes to 4. With the same map and "Cara", a new key appears with 1 vote. With an empty map and "Max", the very first vote gives {"Max": 1}.
Approach
Both cases, a known name and a new name, follow one rule: new count = old count + 1, where the old count of a missing name is 0. So do not branch at all. Read the old count with a default of 0, add 1, and store it back. insert adds the key when it's new and replaces the value when it exists.
Step by step
- Read the current count into
count. Look the name up withvotes.get(&name), which gives anOption:Somereference for a known name andNonefor a new one. Applyunwrap_or(&0)to take the reference or fall back on a reference to 0, and put*in front to read the number behind it as a plaini32. - Call
votes.insert(name, count + 1). For a new name this adds the key with 1 vote. For a known name it replaces the old count. Sincegetonly borrowedname, the string can move into the map here. - Return
votes. Only one entry changed.
The solution in Rust
impl Solution {
fn count_vote(mut votes: HashMap<String, i32>, name: String) -> HashMap<String, i32> {
let count = *votes.get(&name).unwrap_or(&0);
votes.insert(name, count + 1);
return votes;
}
}
The step order matters in Rust. count is a copied number, so nothing still borrows votes or name when insert runs. With the *, count is a plain i32 and the map is no longer borrowed once the line ends. Without it, count would be a &i32, a reference into the map, which only works here because it is last used inside the insert call; copying the number out keeps the code simple. Reading the map through get never adds a key; only the insert does.
Following two calls
For Ana with 3 votes, get finds the entry and count is 3, so insert stores 4 under "Ana", replacing the 3. For Cara, who isn't in the map, get gives None, the default makes count 0, and insert adds "Cara" with 1. The same two lines of code handle both people. This read, add one, write back pattern is the usual way to count things in a map.
Edge cases and common mistakes
- A new name. Indexing with
votes[&name]would panic for a missing key. Thegetwith a default avoids that. - Setting instead of adding.
votes.insert(name, 1)would reset Ana's 3 votes to 1. - Using
nameafterinsert. The string has moved into the map. Using it again is the compile error "borrow of moved value". - An empty map. It works with no special case: the lookup finds
Noneand the default gives 0.
Testing
The lesson's tests print the map as key: value lines sorted by key. Results from running the function:
{"Ana": 3, "Ben": 5},"Ana"printsAna: 4andBen: 5.{"Ana": 3, "Ben": 5},"Cara"printsAna: 3,Ben: 5andCara: 1.{},"Max"printsMax: 1.
