Start learning free

LESSON 229 OF 679 · LOOPS WITH STRINGS, ARRAYS & DICTIONARIES

Looping Over a HashMap in Rust

Lesson Read the full explanation

TL ; DR:

A for loop over a borrowed HashMap, like &stock, visits every entry. (fruit, amount) splits each one into its key and its value.


let stock = HashMap::from([("apples", 5), ("pears", 2)]);

for (fruit, amount) in &stock {
    println!("{} {}", fruit, amount);
}

// pears 2
// apples 5   (in any order)


Full lesson:

A loop can visit every entry of a map, one at a time. The loop body runs once per key.

&stock borrows the map for the loop. Each turn gives a key and its value. The key comes first, then the value.

Both come as references, as in a loop over &nums. Write *amount to get the number itself.

Loop over stock.keys() to get only the keys. Don't need the key? Write _ in its place, as in for (_, amount) in &stock.

The order: a HashMap has no fixed order. It can change from one run to the next. A total or a count comes out the same in any order. Those are safe answers.

Watch out: without the &, the loop takes the map for itself. You can't use stock after such a loop.


Your task

A game keeps each player's points in scores. The variables total and count start at 0.

Loop over scores. Add every player's points to total. Count the players with 10 points or more in count.

The loop needs only the points, not the names. The code at the bottom prints both.

Expected output:

43
2
STEP 1 OF 3
    Ctrl + Enter to check

    RUST · LOOPS WITH STRINGS, ARRAYS & DICTIONARIES

    Looping Over Dictionaries in Rust, explained

    Visiting every entry

    A HashMap stores pairs: a key and the value that goes with it. A for loop over the map visits one pair per turn, and the loop variable can be split into the key and the value right in the loop header:

    let stock = HashMap::from([("apples", 5), ("pears", 2)]);
    
    for (fruit, amount) in &stock {
        println!("{} {}", fruit, amount);
    }
    

    This prints pears 2 and apples 5 in one run. Another run may print them the other way round. The key always comes first in the pair, then the value.

    (In a file of your own, add use std::collections::HashMap; at the top. The lesson's editor already has it.)

    Why the & matters

    Writing &stock borrows the map for the loop. Without the &, the loop takes the map for itself and uses it up. After such a loop, stock is gone, and using it is a compile error:

    error[E0382]: borrow of moved value: `stock`
    ...
    4 |     for (fruit, amount) in stock {
      |                            ----- `stock` moved due to this implicit call to `.into_iter()`
    ...
    7 |     println!("{}", stock.len());
      |                    ^^^^^ value borrowed here after move
    

    The compiler's own suggestion is the fix: loop over &stock.

    Keys and values come as references

    When you loop over &stock, both parts of each pair are references into the map, not copies. To use the number itself, write *amount. Comparing a reference with a plain number does not compile:

    error[E0308]: mismatched types
    11 |         if points >= 10 { t2 += 1; }
       |                      ^^ expected `&_`, found integer
    help: consider dereferencing the borrow
    11 |         if *points >= 10 { t2 += 1; }
    

    Arithmetic such as total += points also happens to work on a reference, but writing *points everywhere is clearer and always works.

    Only the keys, only the values

    You don't have to unpack both parts. Put _ where you don't need one, as in for (_, amount) in &stock. Or ask the map for just one side:

    let mut s = 0;
    for amount in stock.values() {
        s += amount;
    }
    println!("{}", s);
    
    let mut keys: Vec<&&str> = stock.keys().collect();
    keys.sort();
    println!("{:?}", keys);
    

    These print 7 and ["apples", "pears"]. The keys here are &str values and keys() hands out references to them, hence the double & in the type.

    Changing values while you loop

    To change the values, borrow the map mutably with &mut. Each value then arrives as a &mut reference, and *amount += 1 changes the entry in the map itself. On a copy of stock this turns the entries into ("apples", 6) and ("pears", 3). The map must be declared with let mut for this to compile.

    The order is not fixed

    A HashMap has no fixed order, and Rust may even change it from one run to the next. So loop over a map when the answer doesn't depend on the order: a total, a count, a check. When you need a stable order, collect the pairs into a vector and sort it:

    let mut fruits: Vec<_> = stock.iter().collect();
    fruits.sort();
    for (fruit, amount) in fruits {
        println!("{}: {}", fruit, amount);
    }
    

    This always prints apples: 5, then pears: 2.

    The lesson's task

    The map scores holds each player's points. The task is to add all points to total and count the players with 10 points or more in count. Only the points matter, so the key is skipped with _:

    let scores = HashMap::from([("Ana", 12), ("Ben", 7), ("Cleo", 15), ("Dan", 9)]);
    let mut total = 0;
    let mut count = 0;
    
    for (_, points) in &scores {
        total += *points;
        if *points >= 10 {
            count += 1;
        }
    }
    
    println!("{}", total);
    println!("{}", count);
    

    The output is 43 and 2: 12 + 7 + 15 + 9 is 43, and only Ana and Cleo have 10 or more. A total and a count come out the same whatever order the players are visited in.

    Key takeaways

    • for (key, value) in &map visits every entry once and leaves the map usable afterwards.
    • Without the &, the loop moves the map away.
    • Both parts are references: use *value to get the number.
    • Use _ for a part you don't need, or keys() and values() for one side.
    • The order is not fixed, so sort first when it matters.
    Keep going with Rust500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free