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 and when

The structured statements, the indexed and iterating for forms, and when — the one match construct, an exhaustive pattern expression. switch/case/default do not exist (each is a reserved word whose error names when).

Statements

  • if (cond) { .. } else if (..) { .. } else { .. } — braces required.
  • while (cond) { .. }.
  • return expr?; — expr is required unless the function returns nil.
  • break / continue exist for loops; there are no labels and no do..while.

for — two forms

Iterating form — vecs, fixed arrays, slices, strings (one-codepoint strs per step), bytes (u8 per step), and any type with a registered Iterator impl (see Traits and dispatch):

for (let x of expr) { .. }

Indexed form — the induction variable is loop-owned: the update clause and the body may assign it without mut; it is not a normal binding:

for (let i = 0; i < n; i += 1) { .. }

when

when (x) { pattern -> body, ... } is an expression: the first matching arm’s body produces its value. No fallthrough; exactly one arm runs.

use ink::{ Logger };

fn describe(n: i32) -> str {
    return when (n) {
        0         -> "zero",
        1, 2, 3   -> "small",   // comma-separated alternatives
        else      -> "big",     // non-enum scrutinee: else REQUIRED
    };
}

pub fn main() {
    let log = Logger.new("t");
    log.info(f"{describe(0)} {describe(2)} {describe(10)}");
}
zero small big

Patterns

PatternExample
enum member (dotted path)Light.Green
literal0, 3.5, true, "text"
negative literal-1
alternatives (comma list)1, 2, 3
wildcard else / _else -> ..

Ranges, destructuring, binding patterns, and guards (x if cond) do not exist; patterns are enum members, literals, alternatives, and the wildcards only.

The laws

  • Exhaustiveness is always enforced. An enum-typed scrutinee must cover every member (then else is optional) or add else — a partial when is a compile error. Any other scrutinee type (integers can’t be enumerated): else is mandatory.
  • All arms must agree on one type — that is the when’s type. Used as a statement, that type must be nil (arm bodies that are evaluated for effect are written as block arms).
  • Duplicate patterns, and arms made unreachable by earlier ones, are compile errors.
  • Arms use ->. Expression arms are comma-separated; block arms may omit the trailing comma (the corpus writes it).
  • when literal patterns match compile-time values, never runtime ==.
use ink::{ Logger };

enum Light { Green, Yellow, Red }

fn go() { let log = Logger.new("t"); log.info("go"); }
fn brake() { let log = Logger.new("t"); log.info("brake"); }
fn stop() { let log = Logger.new("t"); log.info("stop"); }

pub fn main() {
    let l = Light.Yellow;
    when (l) {
        Light.Green  -> { go(); },
        Light.Yellow -> { brake(); },
        Light.Red    -> { stop(); },   // every member: else optional
    }
}
brake

A when is also the idiomatic nullable guard, together with != nil:

use ink::{ Logger };

struct Point { x: i32; y: i32; }

pub fn main() {
    let log = Logger.new("t");
    let p: ?Point = Point { x: 7, y: 0 };
    when (p != nil) {
        true -> { log.info(f"{p.x}"); },
        else -> { /* absent */ },
    }
}
7