Evaluation Order
Source-order evaluation, short-circuiting, assignment order, defers, cleanup, and operator precedence.
Evaluation order is part of the current language semantics. Except for constructs that short-circuit or select one branch, subexpressions are evaluated left-to-right in source order, and each subexpression's side effects complete before the next subexpression begins.
- Unary operators evaluate their operand before applying the operator.
- Casts evaluate the value before performing the checked conversion.
- Binary operators evaluate the left operand before the right operand.
&&evaluates the right operand only when the left operand is truthy.||evaluates the right operand only when the left operand is falsy.condition ? then_expr : else_exprevaluates the condition first, then only the selected branch.- A call evaluates the callee expression first.
- A method call evaluates the receiver before all explicit arguments.
- Call arguments are evaluated in parameter order after named and default arguments are normalized.
- Struct literal fields are evaluated in struct declaration order, including default field values.
- Array, vector, and tuple literal elements are evaluated left-to-right.
- Member access, optional member access, and subscript expressions evaluate the receiver or base expression first.
- Subscript index expressions are evaluated left-to-right after the base expression.
Example:
import std.io;
fn log(label: str, value: i32) -> i32 {
io.println("{label}");
return value;
}
fn main() {
let result = log("left", 1) + log("right", 2);
io.println("result={result}");
}
Output:
left
right
result=3
Short-circuiting evaluates only the branch it needs:
fn mark(flag: &bool) -> bool {
flag = true;
return true;
}
fn main() {
let right = false;
let ok = false && mark(&right);
assert !ok;
assert !right;
}
Assignment evaluates the target first, including the receiver, base, member path, and index expressions needed to identify the destination. It then evaluates the right-hand side and stores the result.
Compound assignment evaluates the target, loads the old value, evaluates the right operand, computes the new value, and stores it.
fn main() {
let values = [1, 2, 3];
let i = 0;
values[i] += 10;
assert values[0] == 11;
}
A declaration initializer evaluates its right-hand expression before the binding is initialized. Tuple destructuring evaluates the tuple expression once, then initializes bindings left-to-right from tuple elements.
defer registers its body when control reaches the defer statement. When a
block exits normally, defers registered in that block run in last-in,
first-out order before automatic cleanups for locals whose lifetime ends at
that block.
return, break, continue, throw, and error propagation unwind exited
scopes with the same defer-before-cleanup ordering before transferring control.
try expr evaluates expr, unwraps success, and returns the error from the
current function on failure.
expr catch { ... } evaluates expr. On success it yields the unwrapped value;
on error it evaluates only the matching catch arm.
await expr evaluates expr, consumes the resulting future or scoped task,
waits for completion, and yields the result.
These statement forms require a trailing semicolon:
letandconstreturnthrowassertbreakcontinueasm- ordinary expression statements
These statement forms do not take a trailing semicolon:
deferifwhileloopforwithselect- nested block statements
Statement-form match and statement-form block catch may be written with or
without a trailing semicolon. When either form appears inside a larger
statement, the outer statement follows its own semicolon rule.
Higher precedence binds more tightly.
| Precedence | Operators and forms | Grouping |
|---|---|---|
| 14 | (), [], ., ?. | postfix/member, left-to-right |
| 13 | prefix !, ~, +, -, *, &, try, await | prefix |
| 12 | as | left-to-right; takes a type on the right |
| 11 | *, /, % | left |
| 10 | ..., ..< | left |
| 9 | +, - | left |
| 8 | <<, >> | left |
| 7 | <, >, <=, >= | left |
| 6 | ==, != | left |
| 5 | & | left |
| 4 | ^ | left |
| 3 | | | left |
| 2 | && | left, short-circuit |
| 1 | ? :, ||, catch | ternary right; || and catch left |
| 0 | assignment forms | right |
Because ? :, ||, and catch share the lowest
non-assignment precedence, parenthesize expressions that mix them unless that
grouping is intended.