4. Control Flow
This chapter covers branching (if, match), looping (while, for, loop), and destructuring. The key point is that if, match, and loop in rok are all expressions that produce values.
if is an expression
The condition of an if needs no parentheses, and the body is always written in braces. The last expression of each branch becomes the value of the whole if, so there is no separate ternary operator.
let temp = 23;
let feel = if temp >= 28 {
"hot"
} else if temp >= 15 {
"mild"
} else {
"cold"
};
println(f"{temp} degrees: {feel}");
23 degrees: mild
When used as a value, all branches must have the same type. To mix different types, annotate the receiving variable with a sum type such as i32 | str.
match and patterns
match compares a value against patterns from top to bottom and runs the first arm that matches. You can use literals, several patterns joined with |, ranges (4..=9), bindings (k @ pattern), guards (if condition), and the wildcard _.
fn describe(n: i32) -> str {
match n {
0 => "zero",
1 | 2 | 3 => "small",
k @ 4..=9 => f"digit {k}",
k if k < 0 => "negative",
_ => "large",
}
}
for n in [0, 2, 7, -5, 42] {
println(f"{n}: {describe(n)}");
}
0: zero
2: small
7: digit 7
-5: negative
42: large
The compiler checks that every case is handled. i32 has too many values to list, so a final _ is required. Tuples can also be destructured and matched with patterns.
fn locate(p: (i32, i32)) -> str {
match p {
(0, 0) => "origin",
(x, 0) => f"on the x-axis at {x}",
(0, y) => f"on the y-axis at {y}",
(a, b) if a == b => "on the diagonal",
_ => "somewhere else",
}
}
println(locate((0, 0)));
println(locate((5, 0)));
println(locate((2, 2)));
println(locate((1, 9)));
origin
on the x-axis at 5
on the diagonal
somewhere else
When the value can be one of several types (i32 | str | none), match splits on its type with patterns such as n: i32. Those patterns, along with is, if let, and let else, come with sum types in the next chapter.
while and for
while repeats as long as the condition is true. for takes elements one at a time from a range, list, or iterator. a..b excludes b; a..=b includes it. continue moves to the next iteration, and break leaves the loop.
let i = 0;
while i < 3 {
print(f"{i} ");
i += 1;
}
println("");
for k in 0..10 {
if k % 2 == 1 { continue; }
if k > 6 { break; }
print(f"{k} ");
}
println("");
for (idx, name) in ["ada", "grace"].iter().enumerate() {
println(f"{idx}: {name}");
}
0 1 2
0 2 4 6
0: ada
1: grace
while let repeats as long as the pattern matches. pop() returns T | none, which makes draining a list until it is empty simple.
let stack = [1, 2, 3];
while let top: i32 = stack.pop() {
print(f"{top} ");
}
println("");
3 2 1
loop and break with a value
loop repeats forever until a break. If you exit with break value, that value is the result of the loop expression. This suits computations whose result is determined at the moment they finish.
let n = 27;
let steps = 0;
let total = loop {
if n == 1 { break steps; }
n = if n % 2 == 0 { n / 2 } else { 3 * n + 1 };
steps += 1;
};
println(f"Collatz(27) reaches 1 in {total} steps");
Collatz(27) reaches 1 in 111 steps
Labels
To end an outer loop directly from a nested one, attach a 'name: label and use break 'name or continue 'name. Labels on loops and blocks can also carry a value out.
let pair = 'search: loop {
for a in 1..10 {
for b in a..10 {
if a * b == 42 { break 'search (a, b); }
}
}
break (0, 0);
};
println(f"{pair:?}");
let limit = 50;
let checked = 'check: {
if limit > 100 { break 'check -1; }
limit * 2
};
println(f"{checked}");
(6, 7)
100
Destructuring
The left side of let and for can be any pattern that always matches (an irrefutable pattern). Unpack nested tuples, collect the rest with .., or discard with _.
let ((a, b), c) = ((1, 2), 3);
let (first, ..rest) = (10, 20, 30, 40);
let (_, second) = ("skip", "keep");
println(f"{a} {b} {c} {first} {rest:?} {second}");
let points = [(1, 2), (3, 4)];
for (x, y) in points {
println(f"({x}, {y}) -> {x + y}");
}
1 2 3 10 (20, 30, 40) keep
(1, 2) -> 3
(3, 4) -> 7
Summary
if,match,loop, and blocks are expressions that produce values. Branches must have the same type.matchsupports literals,|, ranges,@bindings, and guards, and checks exhaustiveness.while letrepeats while the pattern matches.for x in a..b/a..=biterates over ranges;.iter().enumerate()iterates with indices.break valueand'label:let you exit nested loops and return a result.