Skip to content

Control flow ​

if, and the block, are expressions ​

talor
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 ​

talor
fn count_up(n: i64): i64 {
    let i: i64 = 0;
    while i < n {
        i += 1;
    }
    i
}
talor
// `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
}
talor
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:

talor
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 ​

talor
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
}
PatternMatches
_anything, binds nothing
nameanything, 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 | qeither 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 condpattern plus guard, only at the top of an arm

Three rules give match its safety:

  • Matches are exhaustive. A non-exhaustive match is 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 binds OS_LINUX and leaves two unreachable arms. The way to compare against a constant is if 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 takes parameter 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.

Talor v0.1.0 - Released under the MIT OR Apache-2.0 license.