Start learning free

LESSON 139 OF 679 · DICTIONARIES - CHALLENGES

Add a Contact in Rust: contains_key and insert

Practice Read the full explanation

You get a phone book phoneBook, a HashMap from names to phone numbers. You also get a name and a phone number.

Add name with its number to the phone book, and return the phone book.

Only add name when it isn't in the phone book yet. A name that is already there keeps its old number. The order of the entries doesn't matter.

 

Example 1:

Input: phoneBook = {"Ana": "555-0142", "Ben": "555-0199"}, name = "Cara", number = "555-0123"
Output: {"Ana": "555-0142", "Ben": "555-0199", "Cara": "555-0123"}
Explanation: "Cara" isn't in the phone book yet, so she is added.

Example 2:

Input: phoneBook = {"Ana": "555-0142", "Ben": "555-0199"}, name = "Ben", number = "555-0100"
Output: {"Ana": "555-0142", "Ben": "555-0199"}
Explanation: "Ben" is already in the phone book. He keeps his old number, "555-0199".

Example 3:

Input: phoneBook = {}, name = "Max", number = "555-0100"
Output: {"Max": "555-0100"}
Explanation: The phone book was empty. Now it has one contact.

 

Constraints:

  • phoneBook can be empty.
  • Names are single words made of English letters.
  • Every phone number has three digits, a dash and four digits, like "555-0142".
  • Two people can share a phone number.
STEP 1 OF 3
    Ctrl + Enter to check

    RUST · DICTIONARIES - CHALLENGES

    Add Contact in Rust, explained

    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

    1. The signature marks phone_book as mut, so the function may change it. Write if !phone_book.contains_key(&name). The ! means "is not a key". Passing &name only borrows the string, so name is still yours for the next step. Write the closing } right away.
    2. Inside the if, call phone_book.insert(name, number). Both strings move into the map, which is fine because they aren't used again.
    3. After the if, return phone_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, insert would 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_key is false, 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
    
    Keep going with Rust500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free