Start learning free

LESSON 128 OF 679 · ARRAYS - CHALLENGES

Add If Missing in Rust: contains and push

Practice Read the full explanation

You get a shopping list items, a Vec of strings, and a word item. Add item at the end only when it isn't in the list yet. Return the list.

When item is already there, the list stays as it is. The list may start empty.

 

Example 1:

Input: items = ["milk", "eggs"], item = "bread"
Output: ["milk", "eggs", "bread"]
Explanation: The word "bread" is not in the list yet.
It goes at the end.

Example 2:

Input: items = ["milk", "eggs"], item = "milk"
Output: ["milk", "eggs"]
Explanation: The word "milk" is already there.
Nothing is added.

Example 3:

Input: items = [], item = "apples"
Output: ["apples"]
Explanation: The list is empty, so "apples" is new.
It becomes the only item.

 

Constraints:

  • items has 0 to 9 words: it may be empty.
  • item and every word in items are made of lowercase English letters.
STEP 1 OF 3
    Ctrl + Enter to check

    RUST · ARRAYS - CHALLENGES

    Add If Missing in Rust, explained

    Problem overview

    The function add_if_missing receives a shopping list items, a vector of strings, and a word item. It adds item at the end only when the list doesn't already hold it, and returns the list. The list may start empty.

    For ["milk", "eggs"] and "bread", the word is new, so the result is ["milk", "eggs", "bread"]. For ["milk", "eggs"] and "milk", the word is already there, so the list stays as it is. For an empty list and "apples", the result is ["apples"].

    Approach

    Ask the question first, then act. contains tells whether the vector holds a value, and push adds to the end. Put push inside an if that runs only when the answer from contains is false. That way the list can never get the same word twice.

    Step by step

    1. Write if !items.contains(&item). The ! turns "is there" into "is missing". contains wants a reference, so you pass &item. It only borrows the word, so item is still yours afterwards. Write the closing brace of the if right away.
    2. Inside the if, call items.push(item). This runs only for a new word, and the word moves into the vector.
    3. After the if, end the function with items, without a semicolon. When the word was already there, the vector comes back unchanged.

    The solution in Rust

    impl Solution {
        fn add_if_missing(mut items: Vec<String>, item: String) -> Vec<String> {
            if !items.contains(&item) {
                items.push(item);
            }
            items
        }
    }
    

    The order of the two uses of item matters. contains(&item) borrows it for just that call, so push(item) can then take ownership. If you pushed first and checked afterwards, the compiler would reject the code with "borrow of moved value", because a pushed String is no longer yours. The mut in the signature is what lets push change the vector.

    Why the check comes first

    Calling push without asking would append "milk" a second time, giving ["milk", "eggs", "milk"], which the task forbids. A vector has no built-in rule against repeated values, so the program has to enforce it. The check reads the whole vector from the start until it finds a match, so for a long list it takes longer; for lists as short as these, that doesn't matter. If you ever need to check membership very often on a large collection, a set is the usual better fit, but this lesson works with a plain vector.

    Strings and references

    The vector holds owned String values, and the word arrives as a String too, so the types match: &item is a &String, which is what contains expects for a Vec<String>. Values are compared by their text, so two different String objects with the same letters count as equal.

    Edge cases and common mistakes

    • An empty list. contains is false for it, so the word is added and the result has one item.
    • Forgetting the !. Without it, the word is pushed only when it is already there, which is the opposite of the task.
    • Forgetting the &. items.contains(item) is a type error: contains takes a reference.
    • Putting items inside the if. The return value must come after it, or a word that is already there would return nothing.

    Testing

    Results from running the function:

    • ["milk", "eggs"], "bread" gives ["milk", "eggs", "bread"].
    • ["milk", "eggs"], "milk" gives ["milk", "eggs"].
    • [], "apples" gives ["apples"].
    Keep going with Rust500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free