Control Flow 🚦
So far your programs run top to bottom, every line, once. Real programs make choices and repeat things. Time to steer.
Making decisions: if / else
fn main() {
let temperature = 33;
if temperature > 30 {
println!("Beach day! 🏖️");
} else if temperature > 15 {
println!("Nice walking weather.");
} else {
println!("Hot cocoa time. ☕");
}
}
The condition must be a real bool. Rust refuses the "0 means false"
trick from C and JavaScript, which quietly causes bugs there. And notice: no
parentheses needed around the condition. Clean.
Plot twist: if is an expression
Remember the expression secret from Lesson 4? An if produces a
value, so you can assign it directly, with no awkward "declare then maybe assign" dance:
let temperature = 33;
let outfit = if temperature > 30 { "swimsuit" } else { "jacket" };
println!("Wear the {outfit}");
Both branches must produce the same type; the compiler checks, of course.
Repeating: three loops
1. loop: forever, until break
let mut countdown = 3;
loop {
println!("{countdown}...");
countdown -= 1;
if countdown == 0 {
break; // the only way out
}
}
println!("Liftoff! 🚀");
2. while: as long as something is true
let mut fuel = 10;
while fuel > 0 {
println!("Flying... fuel: {fuel}");
fuel -= 3;
}
println!("Time to land.");
3. for: once per item (the one you'll use most)
// Over a range: 1, 2, 3, 4 (the end is excluded!)
for i in 1..5 {
println!("bottle #{i}");
}
// ..= includes the end: 1, 2, 3, 4, 5
for i in 1..=5 {
println!("crab #{i}");
}
// Over a collection: no index bookkeeping, no off-by-one bugs
let snacks = ["kelp", "algae", "plankton"];
for snack in snacks {
println!("Rusty eats {snack}");
}
for. Know the stop condition? while.
Waiting for something unpredictable (like user input)? loop +
break. When in doubt, for.
Sneak peek: match 🔮
Rust has a fourth way to steer: a supercharged decision-maker you'll properly meet in Lesson 9. A taste:
let dice = 4;
match dice {
1 => println!("Ouch, a one."),
2 | 3 => println!("Meh."),
4..=5 => println!("Nice roll!"),
_ => println!("Six! Maximum crab energy! 🦀"),
}
- Expecting
1..5to include 5: it doesn't; use1..=5. - A
whilewhose condition never becomes false: infinite loop. (PressCtrl+Cin the terminal to rescue yourself.) - Using
if x = 5instead ofif x == 5: one=assigns, two compare. Rust catches this; many languages don't.
FizzBuzz, the classic
Print numbers 1 to 20, but for multiples of 3 print Fizz, for
multiples of 5 print Buzz, and for both print FizzBuzz.
(Hint: n % 3 == 0 means "divisible by 3"; % gives the
division remainder. Check the "both" case first!)
Reveal solution
fn main() {
for n in 1..=20 {
if n % 15 == 0 {
println!("FizzBuzz");
} else if n % 3 == 0 {
println!("Fizz");
} else if n % 5 == 0 {
println!("Buzz");
} else {
println!("{n}");
}
}
}
This tiny program is a real (in)famous interview question. You just did it in lesson five. 🎉
Sum of a range
Use a for loop and a mutable total to add up every number from
1 to 100, then print it. (Legend says young Gauss did this in his head.)
Reveal solution
fn main() {
let mut total = 0;
for n in 1..=100 {
total += n;
}
println!("{total}"); // 5050
}
The bouncing countdown
Rewrite Lesson 2's level-up counter properly: start at level 1 and loop until
level 5, printing "Level up! Now level N" each time. Which loop fits best?
Reveal solution
fn main() {
let mut level = 1;
while level < 5 {
level += 1;
println!("Level up! Now level {level}");
}
}
// or, arguably cleaner:
fn main() {
for level in 2..=5 {
println!("Level up! Now level {level}");
}
}