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.