Problem overview
The function add_contact receives a phone book, a HashMap from names to phone numbers, plus a name and a number. It adds the name with its number and returns the phone book, but only when the name isn't a key yet. A name that is already there keeps its old number. The order of the entries doesn't matter.
With {"Ana": "555-0142", "Ben": "555-0199"}, adding Cara with "555-0123" gives three contacts. Adding Ben with "555-0100" changes nothing, and Ben keeps "555-0199". Adding Max to an empty phone book gives a book with one contact.
Approach
Ask first, then add. contains_key says whether a key exists, and insert adds a pair. The catch is that insert on a key that already exists replaces its value, which would overwrite Ben's old number. So the insert goes inside an if that runs only when the name is not yet a key.
Step by step
- The signature marks
phone_bookasmut, so the function may change it. Writeif !phone_book.contains_key(&name). The!means "is not a key". Passing&nameonly borrows the string, sonameis still yours for the next step. Write the closing}right away. - Inside the
if, callphone_book.insert(name, number). Both strings move into the map, which is fine because they aren't used again. - After the
if, returnphone_book. Either it has the new contact, or it is exactly as it was.
The solution in Rust
impl Solution {
fn add_contact(mut phone_book: HashMap<String, String>, name: String, number: String) -> HashMap<String, String> {
if !phone_book.contains_key(&name) {
phone_book.insert(name, number);
}
return phone_book;
}
}
Three Rust details matter. The map owns its keys and values as Strings, so insert takes ownership of name and number: using name after that line is the compile error "borrow of moved value". The contains_key call comes before the move, which is why it compiles. Second, insert returns an Option holding the old value, which this code ignores. Third, return phone_book; with a semicolon is valid Rust, and it does the same as ending the function with phone_book on its own line.
Following two calls
For Cara, contains_key(&name) is false, so ! makes the condition true, the pair goes in, and the book has three contacts. For Ben, contains_key(&name) is true, the condition is false, the body is skipped, and the function returns the book untouched. In both cases the function ends with the same return; the if decides only whether the book changes first. This "check, then change" shape comes up often with maps, so it is worth recognising.
Edge cases and common mistakes
- An existing name. Without the
if,insertwould replace the old number. With Ben and"555-0100", Ben would end up with the new number, and the lesson's tests expect the old one. - An empty phone book.
contains_keyisfalse, so the contact is added. - Forgetting the
!. The insert would then run only for names that are already there. - Same number for two people. That is allowed. Only the name is the key, so numbers may repeat.
Testing
The lesson's tests print the dictionary as key: value lines sorted by key. Results from running the function on the statement's examples:
Ana: 555-0142
Ben: 555-0199
Cara: 555-0123
for Cara added to the first book. Adding Ben with a new number prints:
Ana: 555-0142
Ben: 555-0199
and adding Max to an empty book prints:
Max: 555-0100
