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

Cambrian provides two control flow expressions: if/else for conditional branching and match for pattern matching. Both are expressions – they evaluate to a value.

if / else

The if/else expression evaluates a boolean condition and returns the value of the corresponding branch:

let fee = if amount > threshold { high_fee } else { low_fee };

Basic Form

if condition {
    value_when_true
} else {
    value_when_false
}

Both branches must produce values of the same type when used as an expression.

Chained Conditions

There is no else if. To test several conditions, nest an if inside the else block:

let tier = if amount >= 10000 {
    3
} else {
    if amount >= 1000 { 2 } else { 1 }
};

In Member Transforms

if/else is frequently used in member transforms to compute new state conditionally. Wrap the if in braces so the transform body is a block:

m_high_score: u64 {
    in submit(score) => {
        if score > m_high_score {
            score
        } else {
            m_high_score
        }
    }
}

In Pure Functions

Pure functions commonly use if/else as their body:

pure fn max(a: u64, b: u64) -> u64 {
    if a >= b { a } else { b }
}

pure fn clamp(val: u64, lo: u64, hi: u64) -> u64 {
    if val < lo { lo } else { if val > hi { hi } else { val } }
}

match

The match expression compares a value against a series of patterns and executes the first matching branch:

let description = match status {
    Status::Active => "running",
    Status::Paused => "paused",
    Status::Closed => "finished",
    _ => "unknown"
};

Syntax

match expression {
    Pattern1 => result1,
    Pattern2 => result2,
    _ => default_result
}

Each arm consists of a pattern, the => arrow, and a result expression. Arms are separated by commas.

Matching Enum Variants

The most common use of match is destructuring enums:

enum Command {
    Deposit(U256),
    Withdraw(U256),
    Freeze
}
let response = match cmd {
    Command::Deposit(amount) => amount,
    Command::Withdraw(amount) => amount,
    _ => 0
};

The variable names in data variant patterns (like amount above) bind the associated data for use in the result expression.

Matching Option

Option<T> is commonly matched to handle present and absent values:

let limit = match m_limit {
    some(val) => val,
    none => 0
};

Block Bodies

When a match arm needs multiple computations, use a block:

let result = match action {
    Action::Transfer(to, amount) => {
        let fee = amount * 3 / 100;
        amount - fee
    },
    Action::Refund(amount) => amount,
    _ => 0
};

Wildcard Pattern

The _ pattern matches any value. It is typically used as the last arm to handle all remaining cases:

let digits = match value {
    0 => 1,
    1 => 1,
    _ => 2
};

In Route Actions

There is no action-level match. In a route body, compute a value with let … = match …; and branch on it with an if action:

routes {
    execute(action: Action) => [
        let amount = match action {
            Action::Transfer(value) => value,
            _ => 0
        };
        if amount > 0 => [
            ~> m_recipient with { value: amount }
        ]
    ]
}

Exhaustiveness

A match must be exhaustive. On an enum, Option, or bool, either cover every case or end with a _ => arm; a match on integers always needs a _ => arm. A non-exhaustive match is rejected. A _ after arms that already cover every case is unreachable and draws a warning, as do arms after a _ (or a bare identifier pattern) and duplicate arms.