Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Control flow

Choosing, repeating, and matching.

if / else

if score >= 90 {
    println("A");
} else if score >= 80 {
    println("B");
} else {
    println("C");
}

if is also an expression — it produces a value, so you can assign with it. In expression position it needs an else, and both arms must produce the same type:

let grade = if score >= 90 { "A" } else { "B" };

Because an if is an expression, it can be an arm of another if and the value flows out through both:

let band = if score >= 90 {
    if score >= 97 { "A+" } else { "A" }
} else {
    "B"
};

One small thing the compiler is strict about: an empty if body won’t parse. If you genuinely want a branch that does nothing, put a comment in it or restructure the condition:

if done {
    // nothing to do yet
}

while and loop

let mut i = 0;
while i < 5 {
    println(i);
    i = i + 1;
}

loop { ... } repeats forever until you break:

let mut n = 0;
loop {
    n = n + 1;
    if n >= 3 { break; }
}

break exits the nearest loop; continue skips to the next iteration.

for

for iterates over a range or a collection:

for i in 0..5 {
    println(i);            // 0 1 2 3 4
}

(0..5 is exclusive of the upper bound; 0..=5 includes it.) You’ll use for over real collections once you meet lists and maps in Everyday programs.

match

match compares a value against patterns and runs the first that fits:

fn describe(n: Int) -> String {
    return match n {
        0       -> "zero",
        1       -> "one",
        _       -> "many",
    };
}

_ is the wildcard — “anything else.” Matches must be exhaustive: the compiler rejects a match that doesn’t cover every possibility. For a Bool that means both true and false; for open-ended types it means a _ arm. This is a safety feature — you can’t forget a case and have it silently fall through.

match shines on enums (a type that’s one of several named shapes), which you’ll meet in Records & data. The arms can bind the data carried by each variant.

Blocks have values

A { ... } block’s last expression — written without a trailing ; — is the block’s value. That’s why if/match can be used as expressions, and why a function can end in a bare expression instead of return. A block whose last item does end in ; has value ().

let label = {
    let base = compute();
    base + 1               // block evaluates to this
};

That’s the whole control-flow surface. Next we look at working with text: Strings & text.