Start learning free

LESSON 115 OF 414 · FOR LOOPS - LOOPING OVER ARRAYS

For Loops in Rust: Looping Over a String and a Vec

Lesson Read the full explanation

A for loop is a special type of loop in Rust which allows us to loop over the items of a collection, such as a string or a vector.


Looping through a string:

For example, a string is a sequence of characters, so we can use a for loop to iterate over each character and do something with that character:

let name = "Andy";

for letter in name.chars() {
  println!("{}", letter);
}

Let's break down this code:

  • The for keyword that indicates the start of a for loop.
  • letter is the loop variable. It will take the value of each different item in the collection
  • The in keyword separates the loop variable from the collection we will iterate on
  • name.chars() is the collection to loop over

The output of this code is:

A
n
d
y

As you can see, on each iteration, letter was assigned the value of the next item in the string and inside the loop, we printed that value.


Looping through a vector:

We can also use for loops with lists:

let fruits = vec!["banana", "orange", "pear", "kiwi"];

for fruit in &fruits {
  println!("I eat {}", fruit);
}

The output of this code is:

I eat banana
I eat orange
I eat pear
I eat kiwi

Assignment
Now let's greet our friends using a for loop!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 2
    Ctrl + Enter to check

    RUST · FOR LOOPS - LOOPING OVER ARRAYS

    Looping Through Arrays in Rust, explained

    What a for loop does

    A for loop runs a block of code once for each item of a collection. You don't count positions or check where the collection ends. Rust hands you the items one at a time, in order, and stops after the last.

    Looping over the characters of a string

    A string is a sequence of characters, so a loop can visit each one:

    let name = "Andy";
    
    for letter in name.chars() {
        println!("{}", letter);
    }

    The output is:

    A
    n
    d
    y
    

    The pieces of the loop are:

    • for starts the loop.
    • letter is the loop variable. On each pass it holds the next item. You choose its name.
    • in separates the loop variable from the thing being walked over.
    • name.chars() produces the characters of the string one by one.
    • The braces enclose the body, the code that runs on every pass.

    Rust asks for .chars() here because a string can be viewed as bytes or as characters, and the choice is yours to make. Looping over name.chars() gives characters of type char. You can't write for letter in name directly, since a &str is not itself iterable.

    Looping over a vector

    The same shape works for a Vec:

    let fruits = vec!["banana", "orange", "pear", "kiwi"];
    
    for fruit in &fruits {
        println!("I eat {}", fruit);
    }

    This prints:

    I eat banana
    I eat orange
    I eat pear
    I eat kiwi
    

    On the first pass fruit is "banana", on the second "orange", and so on. The body uses fruit to build a different message each time, which is exactly what the hand-written version could not do without repeating itself.

    Why the ampersand matters

    The loop is written over &fruits, not fruits. The & borrows the vector for the duration of the loop. Without it, the loop takes ownership of the vector and consumes it. The vector is then moved into the loop and can't be used after it. This program:

    let friends = vec!["Andy", "Mary", "John"];
    for friend in friends {
        println!("{}", friend);
    }
    println!("{:?}", friends);

    doesn't compile. The compiler reports borrow of moved value: `friends` and suggests iterating over a slice of the vector's content, that is, adding the &. Writing for friend in &friends leaves the vector usable after the loop. For a fixed-size array such as [1, 2, 3] the plain form also compiles, because arrays of copyable numbers can be copied, but borrowing is the habit that works for vectors.

    When you also need the position

    A plain loop doesn't tell you which pass you are on. If you need the position as well, wrap the iterator in enumerate(), which yields pairs of an index and an item:

    for (i, friend) in friends.iter().enumerate() {
        println!("{} {}", i, friend);
    }

    With the friends above this prints 0 Andy, 1 Mary and 2 John. The lesson's exercise doesn't need the index, so the plain loop is the better choice there: fewer moving parts, and no index to get wrong.

    The exercise

    The editor starts with a vector of friends:

    let friends = vec!["Andy", "Mary", "John"];

    The task is to greet each friend, in two steps.

    Step 1. Write a for...in loop whose loop variable is named friend and which iterates over &friends.

    Step 2. Inside the loop, print Hey, , the name and !. Use the format string "Hey, {}!" and pass friend as the argument that fills the {}.

    The reference solution is:

    let friends = vec!["Andy", "Mary", "John"];
    for friend in &friends {
        println!("Hey, {}!", friend);
    }

    It prints Hey, Andy!, Hey, Mary! and Hey, John!, each on its own line. The body runs three times because the vector has three items.

    Common mistakes

    • Leaving out the & and then using the vector after the loop.
    • Calling the loop variable one thing and using another in the body. Inside the loop only friend exists, so writing friends there would give you the whole vector, which {} can't print.
    • Using for x in string without .chars().
    • Putting the print after the closing brace. It then runs once, not once per item, and the loop variable is out of scope there.

    Key takeaways

    • for item in collection runs its body once for every item, in order.
    • Use text.chars() to walk the characters of a string.
    • Use &vec to loop over a vector without giving it up.
    • Each {} in println! is filled by the next argument.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free