The braces decide what repeats
A Rust for loop repeats whatever sits between its braces. Lines inside the braces form the loop body and run once for every item. Lines outside run on their own schedule: before the loop if they come before it, after it if they come after.
Take this code:
let languages = vec!["Python", "Java", "JavaScript"];
for language in &languages {
println!("I love {}", language);
println!("I want to get better at it");
}
Both println! calls are inside the braces, so both run for every language:
I love Python
I want to get better at it
I love Java
I want to get better at it
I love JavaScript
I want to get better at it
Now move the second print out of the loop:
let languages = vec!["Python", "Java", "JavaScript"];
for language in &languages {
println!("I love {}", language);
}
println!("I want to get better at them");
Only the first print is in the body. The second one runs once, after the loop has finished:
I love Python
I love Java
I love JavaScript
I want to get better at them
Indentation helps you read this, but in Rust it doesn't decide anything. The compiler reads the braces. A line that is indented like the body but sits after the closing brace is still outside the loop.
Execution flow
A program starts at its first line and runs statements from top to bottom, one at a time. This order is called the execution flow. A loop doesn't break it. When the flow reaches a for, it runs the body once per item, then moves on to the line after the closing brace.
Here is a longer example:
let fruits = vec!["banana", "apple", "kiwi"];
println!("I'm hungry!");
for fruit in &fruits {
println!("I'm gonna eat:");
println!("{}", fruit);
}
println!("I love my life!");
The flow is: print I'm hungry! once, run the body for banana, run it for apple, run it for kiwi, then print I love my life! once. The output shows it:
I'm hungry!
I'm gonna eat:
banana
I'm gonna eat:
apple
I'm gonna eat:
kiwi
I love my life!
Each statement in the body appears three times, once per fruit, and the two outside statements appear once each. Counting this way is the habit to build: for every line, ask whether it is inside the braces or not.
The exercise
The editor holds this code, and the program should print four lines:
Today we'll learn about:
Python
Java
JavaScript
Let's code!
The first and last lines come from the existing println! statements. The three language names must come from a loop placed between them. The step is to add a for loop whose loop variable is named language and which iterates over &languages, a borrowed reference to the vector, so the vector stays usable after the loop. Inside it, print the variable with the format string "{}".
The loop goes between the two prints because of the execution flow: the heading must come before the items, and the closing line after them. If you put the loop above the first print, the names would come out before the heading. The reference solution is:
let languages = vec!["Python", "Java", "JavaScript"];
println!("Today we'll learn about:");
// Write the for loop below:
for language in &languages {
println!("{}", language)
}
println!("Let's code!");
Details and common mistakes
- The
println!inside the loop has no semicolon in the reference. A semicolon is optional after the last statement of a block when its value is(), as it is here, and adding one is equally fine. println!needs a format string.println!(language)doesn't work; writeprintln!("{}", language).- Writing
for language in languageswithout the&moves the vector into the loop, so it can't be used after it. - Placing the loop after
println!("Let's code!")prints the languages last. The order of the lines in the code is the order of the output.
Key takeaways
- The braces of a
forloop mark its body, and only the body repeats. - Code before the loop runs once before it, and code after the loop runs once after it.
- Programs run top to bottom, and a loop just repeats a stretch of that flow.
