Control Flow

All control flow constructs are statement-based. They do not produce values. Results come from assignments inside the branches or from return.

Conditions must be bool. There is no truthiness: if x where x is an int is a type error.

if

if n < 0 {
    Sys.print("negative")
} else if n == 0 {
    Sys.print("zero")
} else {
    Sys.print("positive")
}

No parentheses around the condition. Braces are required.

while

mut i = 0
while i < 10 {
    i += 1
}

loop

loop is an explicit infinite loop. Exit with break:

loop {
    mut line = Sys.read_line().unwrap_or("")
    if line == "quit" { break }
    handle(line)
}

This reads clearer than while true when the exit condition is in the middle of the body.

for -- counting

The C-style counting loop. Each clause is separated by semicolons:

for i = 0; i < 10; i += 1 {
    Sys.print(f"step {i}")
}

The loop variable (i here) is a fresh mutable binding scoped to the loop. Any clause can be empty. The most common use is iterating over an index.

for-in -- iteration

Walk lists and maps with for...in:

names = ["alice", "bob", "carol"]
for name in names {
    Sys.print(name)
}

For maps and for lists when you need the index, use two variables:

for i, x in names {
    Sys.print(f"{i}: {x}")
}

scores = ["alice": 95, "bob": 87]
for name, score in scores {
    Sys.print(f"{name} scored {score}")
}

match

match branches on an enum value. Every variant must be covered, or you get a compile error. Use _ to match anything you do not need to handle individually:

enum Status { Active, Pending, Closed(str) }

match status {
    Active     => { Sys.print("active") }
    Pending    => { Sys.print("pending") }
    Closed(r)  => { Sys.print(f"closed: {r}") }
}

Payload variants bind their data as new variables in the arm body. There are no guards in match arms. If you need a condition, use an if inside the arm:

match result {
    Ok(n) => {
        if n > 100 {
            Sys.print("big")
        } else {
            Sys.print(f"got {n}")
        }
    }
    Err(msg) => { Sys.print(f"error: {msg}") }
}

match on Option<T> is very common:

match find_user(id) {
    Some(user) => { greet(user) }
    None       => { Sys.print("not found") }
}

break

break exits the nearest enclosing loop. There is no continue.

for x in items {
    if x < 0 { break }
    process(x)
}

To skip an item without exiting the loop, wrap the body in an if:

for x in items {
    if x > 0 {
        process(x)
    }
}

return

return exits the current function and optionally passes a value back:

classify(n: int): str {
    if n < 0  { return "negative" }
    if n == 0 { return "zero" }
    return "positive"
}

A function that returns unit can use a bare return to exit early:

log(msg: str): unit {
    if .quiet { return }
    Sys.print(msg)
}