Skip to content

Control Flow

Cerune uses if for conditional execution, while and for for loops, and match to branch on sum types (enum).

An if condition must have type bool. The else branch is optional.

value: i64 = 7;
if value < 10 {
print(value);
} else {
print(10);
}

if is a statement, not a value-producing expression. Numeric conditions are not implicitly converted to bool.

while evaluates its bool condition before every iteration. If the initial condition is false, the loop body does not execute.

mut count: i64 = 0;
while count < 3 {
print(count);
count = count + 1;
}

The standard for syntax requires an initializer, a continuation condition, and an update statement.

mut total: i64 = 0;
for (mut i: i64 = 0; i < 3; i = i + 1) {
total = total + i;
}
print(total); // 3

The initializer executes once, the continuation condition before each iteration, and the update after the body completes. Bindings declared by the initializer are scoped to the loop. The initializer can also reassign an existing mut binding.

Both fixed-length and dynamic arrays can be iterated in element order.

values: [i64; 3] = [4, 7, 9];
for (index: i64, value: infer in values) {
print(index);
print(value);
}

Omit the index with for (value: infer in values). The subject is evaluated once and copied at loop entry. Changes to the original array do not affect the elements visited in that loop. Each element is copied into its iteration binding.

See Arrays for array types and copy semantics.

  • break; exits the innermost loop.
  • continue; skips the remaining body of the current iteration.
  • In while, continue proceeds to condition evaluation.
  • In standard for, continue runs the update statement before checking the condition.
  • In for … in, continue advances to the next element.

Neither statement can be used outside a loop. Labeled forms targeting an outer loop are not implemented.

match selects behavior for an enum variant. Its subject is evaluated and copied once, and arms are considered in source order.

enum Lookup { Found { text: string }, Missing }
value: Lookup = Lookup::Found { text: "result" };
match value {
Lookup::Found { text: text } => { print(text); },
Lookup::Missing {} => { print("missing"); },
}

match can be used as either a statement or an expression. Statement arms have the form => { statements... }; expression arms use => expression. For an expression, every arm must produce a compatible result type.

label: string = match value {
Lookup::Found { text: text } => text,
Lookup::Missing {} => "missing",
};

An arm may include a bool guard with if condition after its pattern. A guard runs only when the variant matches. Every variant requires an unguarded arm. Whole-value wildcard arms and nested patterns are not currently supported.

&& and || use short-circuit evaluation. When the left operand determines the result, the right operand is not evaluated.

count: i64 = 0;
print(count != 0 && 12 / count > 2); // false

Each braced block creates a new scope. Bindings declared inside it are not visible outside it. See Variables and Bindings for details.

This page summarizes the specification. Consult the language reference (primary source) for exact evaluation order, match exhaustiveness, and branch constraints.