Control flow
Control flow covers conditionals, loops, dispatch, cleanup, and error-control
forms.
Conditionals
if(cond){ a } else { b }
if(cond){ a } elif(other){ b } else { c }
if cond { a } elif other { b } else { c }
if(def x = value x > 0){ a } else { b }
def value = if(cond){ a } else { b }
The parser accepts parenthesized conditions and whitespace-separated
conditions.
Use if as an expression when its branches produce values. The compiler merges
branch types against the surrounding context. In binding or expression
position, each branch needs one value-producing statement, such as a final
expression or a nested value-producing if.
Expression-position if needs an else branch:
def label = if(code == 200){ "ok" } else { "error" }
Conditions use truthiness. nil, false, 0, empty strings, and empty
containers are false. Non-zero numbers, non-empty strings, non-empty
containers, pointers, handles, and callable values are true. Use an explicit
comparison in code that should stay readable under --strict-types.
Loops
while(cond){ body }
while(mut i = 0 i < n ++i){ body }
for item in iterable { body }
for item, index in iterable { body }
for(index in iterable){ body }
for(mut i = 0 i < n ++i){ body }
for item in lo..hi { body }
break
continue
while repeats while the condition stays true. The parser accepts
while(init cond update). Prefer for(init cond update) for counter loops.
for iterates over iterable values such as lists, ranges, strings, and
standard-library iterable helpers. The comma form binds the current value first
and the zero-based index second. The lo..hi range expression is inclusive in
source-level for loops.
The parenthesized for(index in xs){ ... } form binds the zero-based index.
Use it when the body reads xs[index].
mut seen = []
for ch, i in "test" {
seen = seen.append(f"{ch}:{i}")
}
assert(seen == ["t:0", "e:1", "s:2", "t:3"], "indexed loop")
Loop control: break and continue
Use break to immediately exit the innermost enclosing loop.
Use continue to skip the remainder of the innermost loop's body and proceed to the next iteration.
mut sum = 0
for i in 1..10 {
if i % 2 == 0 {
continue
}
sum += i
if sum > 20 {
break
}
}
Case
case is value dispatch.
See patterns.md for full arm syntax and guards.
case value {
literal -> expr
a, b, c -> expr
lo..hi if guard -> expr
lo..hi -> expr
_ -> fallback
}
case handles literal, set-of-literals, and range dispatch. Range arms include
both endpoints and use the same ordered comparisons as >= and <=. case
supports integers, floats, and strings.
Use case in binding position when each selected arm produces a value.
Match
match is pattern dispatch.
See patterns.md for full arm syntax and guards.
match value {
pattern -> expr
Pattern(value) -> expr
_ -> fallback
}
match handles dispatch by value shape. Match ADT variants by qualified
constructor name and positional payload bindings.
Try and catch
try { body } catch err { handler }
try { body } catch(_) { handler }
try catches language/runtime failures. Standard-library APIs may return
structured Result values instead.
Defer
defer { cleanup }
defer schedules cleanup for the current scope. Use it around files, handles,
processes, and native resources. The runtime runs multiple defers in
last-in-first-out order and also runs them during panic unwinding.
Labels and goto
start:
if(done){ goto end }
goto start
end:
Labels name a position inside the current function. goto jumps only to a
label in that function. It can leave inner scopes; the runtime runs pending
defer cleanup on the way out. It cannot jump into a deeper scope because
that would skip binding initialization. The compiler rejects undefined labels.
Use goto for small local state machines and cleanup exits where structured
loops make the control path harder to see. Prefer while, for, break, and
continue for ordinary iteration.
With
with type name = value { body }
with is a type-first resource binding. The colon separates the resource type
from the binding name; do not write with name: Type. Cleanup runs when the
body falls through, returns, or unwinds through a panic. with ptr scopes raw
allocations from malloc.
Related
- patterns.md for
caseandmatcharm shapes. - errors.md for failure behavior.
- runtime.md for cleanup and resource boundaries.