Step 1 of 5 · Reading · ~4 min
Learn
Control Flow
Loops
By the end of this lesson you can repeat work with Rust's three loops (for, while, loop), pick the right range end (.. or ..=), and build a running result across iterations without the classic off-by-one.
for over a range
A range is a sequence of integers written start..end. The for loop hands you each value in turn, binding it to the name after for:
for k in 1..4 {
print!("{} ", k);
}
// prints: 1 2 3
The end of a range is where off-by-one bugs live. Two spellings, one character apart:
| Range | Values produced | Count |
|---|---|---|
1..4 | 1, 2, 3 | 3 (end excluded) |
1..=4 | 1, 2, 3, 4 | 4 (end included) |
0..4 | 0, 1, 2, 3 | 4 |
3..3 | nothing | 0 |
Rule of thumb: .. when the end is a length or a limit you must stay below; ..= when the problem says "from a to b" and means b too.
The accumulator pattern
An accumulator is a variable that lives outside the loop and is updated on every pass, so after the loop it holds the combined result. It changes, so it needs mut. Here it multiplies instead of adds, computing 4 factorial:
let k: i64 = 4;
let mut product: i64 = 1;
for step in 1..=k {
product = product * step;
}
println!("{}", product); // 24
Trace it: product starts at 1; step 1 gives 1, step 2 gives 2, step 3 gives 6, step 4 gives 24. The loop ends, then the single println! runs once. For multiplication the starting value is 1; for addition it would be 0 (the value that changes nothing). x += y is shorthand for x = x + y, and *= works the same way.
Where the pieces sit matters. The same program with the println! moved inside the braces prints 1, 2, 6, 24 on four lines, because everything inside the braces runs once per value. And if you write let mut product = 1; inside the braces, a fresh variable is created on every pass and thrown away at the closing brace, so the outer one never changes.
while: repeat while a condition holds
Use while when you do not know the number of passes in advance, only when to stop:
let mut value: i64 = 1;
let mut doublings = 0;
while value < 1000 {
value *= 2;
doublings += 1;
}
println!("{} {}", doublings, value); // 10 1024
The condition must be a bool and is checked before every pass, including the first.
loop, break and continue
loop repeats forever until a break. Unlike the other two, loop is an expression: break value ends it and hands that value out.
let mut p: i64 = 1;
let first_big = loop {
p *= 3;
if p > 100 {
break p;
}
};
println!("{}", first_big); // 243
continue skips the rest of the current pass and moves to the next one; it works in all three loops.
Big totals need a big type
Totals grow fast. An i32 stops at 2,147,483,647; adding up the first 70,000 whole numbers already passes that. In a debug build Rust panics with attempt to add with overflow; in an optimised build the value silently wraps to a wrong, possibly negative, number. Keep accumulators i64 when inputs can be large.
Common mistakes
- Using
..where the end is included.1..5stops at 4, so a sum of 1 to 5 prints10instead of15. - Printing inside the loop. You get one line per pass (
1,3,6, ...) instead of one final line. - Declaring the accumulator inside the loop. It resets every pass and the value printed after the loop stays
0. - An
i32accumulator. Large N overflows: a panic, or a wrapped, wrong total.
Your exercise
Read a whole number N and print the sum of every whole number from 1 up to and including N. The starter reads N and prints total; you make total an accumulator and write the loop. The most likely mistake is a range that stops one short.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…