03 - Decisions and Loops¶
What this session is¶
About an hour. You'll learn if (an expression in Rust - produces a value), three loop forms (loop, while, for), and the basics of match (Rust's powerful pattern-matching switch).
if is an expression¶
In most languages, if is a statement (does something, returns nothing). In Rust, if is an expression - it produces a value:
Notice no semicolons inside the { } for the branches - the last expression is the value of the branch. Both branches must produce the same type. Then let label = ... captures that value.
The "old" form still works:
Braces are required even for one-line bodies. (Rust prevents the C-style "forgot the braces" bug.)
No parentheses around the condition required:
Comparison operators¶
| Op | Meaning |
|---|---|
== |
equal |
!= |
not equal |
< <= > >= |
numeric |
Strings: == works for content comparison (&str == &str, String == &str). Unlike Java, you don't need .equals - Rust does the right thing.
Chaining: else if¶
let score = 75;
let grade = if score >= 90 { "A" }
else if score >= 80 { "B" }
else if score >= 70 { "C" }
else { "F" };
println!("{grade}");
Combining: &&, ||, !¶
Same as C/Java/Go. Short-circuiting: && skips the right if left is false; || skips if left is true.
Loop 1: loop (infinite)¶
loop runs forever until break. Like while true in other languages but more explicit.
Cool trick: break can return a value:
Loop 2: while¶
Standard "while condition" loop.
Loop 3: for ... in¶
By far the most common. Iterates over anything that produces a sequence:
1..=5 is a range - 1 to 5 inclusive. 1..5 is exclusive (1, 2, 3, 4). You'll see both.
Iterate a collection:
let fruits = ["apple", "banana", "cherry"];
for fruit in fruits.iter() {
println!("{fruit}");
}
// Or simpler:
for fruit in &fruits {
println!("{fruit}");
}
The &fruits is a reference to the array (so for doesn't consume it). We'll explain references properly in page 06. For now, the pattern for x in &collection is idiomatic.
break and continue¶
for i in 1..=10 {
if i == 5 { break; }
if i % 2 == 0 { continue; }
println!("{i}");
}
// prints 1, 3
You can also label loops to break out of nested ones:
Rare; nice when you need it.
match: pattern-matching switch¶
Rust's switch, called match. More powerful than C-style switch - matches on patterns, not just constants. Always exhaustive - the compiler verifies you covered every case.
let day = 2;
let name = match day {
1 => "Mon",
2 => "Tue",
3 => "Wed",
_ => "?", // catch-all (underscore)
};
println!("{name}"); // Tue
The arms (pattern => value) are comma-separated. The whole match is an expression - its value is whatever arm matched.
Multiple values per arm:
Ranges:
let category = match age {
0..=12 => "child",
13..=17 => "teen",
18..=64 => "adult",
_ => "senior",
};
_ is the catch-all pattern - matches anything. Required when the patterns don't cover everything.
match gets much more powerful when matching on enums and structs (page 05).
Exercise¶
In your hello project, replace src/main.rs with FizzBuzz:
For each number 1 to 20:
- Divisible by 3 → print Fizz.
- Divisible by 5 → print Buzz.
- Divisible by both → print FizzBuzz.
- Otherwise → print the number.
Hint: check "both 3 and 5" first.
Then rewrite using match:
match (n % 3, n % 5) {
(0, 0) => println!("FizzBuzz"),
(0, _) => println!("Fizz"),
(_, 0) => println!("Buzz"),
_ => println!("{n}"),
}
(n%3, n%5). (0, 0) means "both zero." (0, _) means "first is zero, second is anything." Powerful pattern.
What you might wonder¶
"Why is if an expression?"
Lets you write let x = if cond { a } else { b }; instead of needing a ternary operator or a separate temp variable. Cleaner; consistent with the "everything is an expression" trend.
"Why does match require _ or full coverage?"
Exhaustiveness checking. If you switch on an enum and forget a variant, the compiler tells you exactly which one. This catches a huge class of "I forgot to handle that case" bugs.
"What about a do-while?"
Use loop with a conditional break at the end:
Done¶
if/elseas expressions.- Three loop forms.
break/continue(and label-break).matchwith multi-value arms, ranges, tuple patterns.