Control flow
if, and the block, are expressions
let sign = if x < 0 { -1 } else if x == 0 { 0 } else { 1 };
let v = { let t = compute(); t * 2 };A block's value is its tail expression; without one, the block has type (). An if used as a value must have an else, and both branches must have the same type. Conditions are bool; no parentheses are required around them.
Loops
fn count_up(n: i64): i64 {
let i: i64 = 0;
while i < n {
i += 1;
}
i
}// `loop` runs until something breaks out of it.
fn first_power_of_two_over(limit: i64): i64 {
let v: i64 = 1;
loop {
if v > limit { break; }
v = v * 2;
}
v
}fn sum_below(n: i64): i64 {
let total: i64 = 0;
for i in 0..n { // half-open integer range, only in `for`
total = total + i;
}
total
}
fn letters(text: string): i64 {
let n: i64 = 0;
for c in text { // the characters of a string, decoded
n += 1;
}
n
}A for over an array yields its elements, and both for and let take a pattern, so a tuple is taken apart where it is bound:
fn main(): i32 {
let scores = [("ana", 7), ("bia", 9)];
let best = 0;
for (name, score) in scores {
if score > best { best = score; }
println(`${name} scored ${score}`);
}
let (low, high) = (1, best);
println(`${low} to ${high}`);
0
}An element of an array the function does not own is a borrow in the loop body, not a copy.
break and continue act on the loop they are written in, and return leaves the function. Braces are mandatory on every body. switch, do/while, C-style for (;;), goto and labeled breaks do not exist.
match
let area = match shape {
Shape.Circle(r) => r * r * PI,
Shape.Rect { w, h } => w * h,
Shape.Empty => 0.0,
};
match token.kind {
TokenKind.Plus | TokenKind.Minus => { /* the body */ } // or-pattern
TokenKind.Integer(n) if n > 100 => { /* the body */ } // guard
_ => { } // wildcard
}| Pattern | Matches |
|---|---|
_ | anything, binds nothing |
name | anything, binds it |
literal (1, 'a', "s", true) | equal value |
Enum.Variant, Enum.Variant(p, q), Enum.Variant { f: p, g } | that variant, sub-patterns on the payload |
Some(p), None, Ok(p), Err(p) | the cases of Option and Result |
(p, q) | tuple |
Struct { f: p, g } | struct destructuring |
p | q | either pattern; both alternatives must bind the same names, and when they bind, the alternation sits at the top of an arm (Case(a) | Case(b)), never inside a payload |
p if cond | pattern plus guard, only at the top of an arm |
Three rules give match its safety:
- Matches are exhaustive. A non-exhaustive
matchis a compile error. - An arm no value can reach is an error too. A bare name is the pattern that binds, not a comparison:
match os { OS_LINUX => ..., _ => ... }with two constants bindsOS_LINUXand leaves two unreachable arms. The way to compare against a constant isif os == OS_LINUX. - Bindings borrow. A name bound by a pattern refers to the matched place for the duration of the arm: Copy payloads are copied, every other payload is read in place. Storing, returning or passing a bound name to a
takesparameter is a move, and borrowing says when that is allowed.
Arms are separated by ,; a block arm needs no comma. All arm bodies must have the same type when the match is used as a value.