Skip to content
Lesson 6 of 15

Step 1 of 5 · Reading · ~4 min

Learn

Control Flow

If Expressions and match

By the end of this lesson you can make a program choose between several outcomes with an if/else if chain or a match, put the most specific case first so it is not shadowed, and use either construct to produce a value.

if: a bool condition, braces, no parentheses

The condition goes straight after if, with no parentheses, and the body must be in braces. The condition must be a real bool: if 1 { } does not compile, because Rust has no "truthy" integers.

let fuel = 12;
if fuel < 10 {
    println!("refuel now");
} else if fuel < 25 {
    println!("refuel soon");
} else {
    println!("fine");
}
// prints: refuel soon

Rust tests the conditions top to bottom and runs the first branch whose condition is true. Every later branch is skipped, even if its condition would also be true.

if is an expression

A block's last expression, written without a semicolon, is the block's value. So an if/else can sit on the right of let, and Rust needs no ?: ternary operator:

let speed = 72;
let verdict = if speed > 70 { "ticket" } else { "ok" };
println!("{}", verdict);   // ticket

Both branches must produce the same type. if speed > 70 { "ticket" } else { 0 } is a compile error: one branch is a &str, the other an integer.

The ordering trap, on leap years

A year is a leap year if it divides by 4, except centuries, except centuries that divide by 400. The rules overlap, so the order of the checks decides the answer:

let year = 1900;
let wrong = if year % 4 == 0 { "leap" }        // 1900 % 4 == 0: stops here
            else if year % 100 == 0 { "common" }
            else { "common" };
let right = if year % 400 == 0 { "leap" }      // most specific rule first
            else if year % 100 == 0 { "common" }
            else if year % 4 == 0 { "leap" }
            else { "common" };
println!("{} {}", wrong, right);   // leap common
yearwrong orderright order
2024leapleap
1900leapcommon
2000leapleap

Because the first true branch wins, a general test placed above a specific one makes the specific branch unreachable. Put the narrowest condition first.

match: patterns, checked for completeness

match compares one value against patterns, top to bottom, and runs exactly one arm. There is no fallthrough and no break. Like if, it produces a value:

let battery = 57;
let icon = match battery {
    0 => "empty",
    1..=20 => "low",           // ..= is an inclusive range
    21..=80 => "ok",
    _ => "full",               // _ matches anything left over
};
println!("{}", icon);          // ok

The compiler demands that the arms cover every possible value. Delete the _ arm above and the build fails with a non-exhaustive patterns error that names the uncovered ranges (the negatives and 81 upward). You cannot forget a case by accident.

You can match two facts at once by putting them in a tuple. With two bools there are exactly four combinations, and the compiler checks that your arms cover all of them:

let weekend = true;
let raining = false;
let plan = match (weekend, raining) {
    (true, false) => "hike",
    (true, true) => "museum",
    (false, _) => "work",
};
println!("{}", plan);          // hike

Remainders of negative numbers

The Numbers lesson said integer division truncates toward zero. The remainder follows it: -7 % 3 is -1, not 2, and -9 % 3 is 0. So x % 3 == 0 is the reliable divisibility test for any sign, while x % 3 > 0 as a "not divisible" test is false for -7 and picks the wrong branch.

Common mistakes

  • General test first. Checking the 3 rule before the 15 rule makes 15, 30 and 45 print Fizz: the first true branch wins and the FizzBuzz branch never runs.
  • Separate ifs instead of one chain. Without else, several branches can run, and 15 prints Fizz and Buzz on separate lines instead of one line.
  • Testing % 3 > 0 for "not divisible". Negative remainders are negative, so -7 comes out as Fizz instead of -7.
  • Forgetting zero. 0 % 3 and 0 % 5 are both 0, so 0 is FizzBuzz, not 0.

Your exercise

The starter reads one integer n and prints it unchanged. Replace that with a single if/else if chain or a match that prints the right word or the number. The mistake to avoid: a general check placed above the specific one.

Up nextLoopsControl Flow

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…