Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Control Flow

If

Inline Conditional

if x > 0 then "positive" else "non-positive"

if condition then expr else expr — both branches required for inline form.

Multi-Arm Conditional

if followed by a block introduces guard-style arms with =>:

fn classify(x: i64) -> String
  if
    x > 0  => "positive"
    x < 0  => "negative"
    else   => "zero"

Each arm is condition => body. Use else => for the default case.

Arms can have multi-statement bodies:

fn process(x: i64) -> i64
  if
    x > 0 =>
      let doubled = x * 2
      doubled + 1
    else => 0

Postfix Conditional

expr if condition — executes expr only if condition is true:

return error.FileNotFound if fd < 0
break if i >= 10

This is the primary guard/early-exit pattern.

Match

Pattern matching with => arms:

fn describe(s: Shape) -> i32
  match s
    Shape::Square.{ side } => side * side
    Shape::Circle.{ r }    => r * r

Literal Patterns

match x
  0 => "zero"
  1 => "one"
  _ => "other"

Data Type Patterns

match result
  Shape::Square.{ s } => s
  Shape::Circle.{ r } => r

Or Patterns

Multiple patterns separated by |:

match x
  1 | 2 | 3 => "small"
  4 | 5 | 6 => "medium"
  _         => "large"

Guards

Add conditions with if after the pattern:

match opt
  Some.{ x } if x > 0 => "positive"
  Some.{ x } if x < 0 => "negative"
  Some.{ _ }          => "zero"
  _                   => "none"

Multi-Statement Arms

match x
  0 =>
    let result = compute()
    result + 1
  _ => x

For

for is the only loop construct. All forms support break to exit and return to exit the enclosing function.

Infinite Loop

Bare for loops forever. Use break or return to exit.

for
  event := poll()
  break if event == quit
  handle(event)

Condition (While-Style)

for cond loops while the condition is true.

mut i := 10
for i > 0
  i <- i - 1
// i is 0 here
mut sum := 0
mut i := 0
for i < 10
  sum <- sum + i
  i <- i + 1
// sum is 45

Numeric Range

for i in start..end iterates from start up to (but not including) end. The loop variable i is bound to the current value.

mut sum := 0
for i in 0..10
  sum <- sum + i
// sum is 45

Collection Iteration

for x in coll iterates over slices, buffers, and fixed arrays. The loop variable x is bound to each element.

for c in text
  process(c)

Collection with Index

for x, i in coll — element first, index second. x is the element type, i is usize.

for c, i in text
  if is_lower(c) then
    buf[i] <- rotate(c, shift)
  else
    buf[i] <- c

Nested fixed arrays compose naturally with loops and indexing.

matrix : [2; [3; i32]] = .{
  .{ 1, 2, 3 },
  .{ 4, 5, 6 }
}

sum : i32 = 0
for row in matrix
  for cell in row
    sum <- sum + cell

matrix[1][2] <- sum

Collection with Filter

for x in coll where cond — only visits elements where the condition is true. The condition can reference the loop variable.

for c in text where is_lower(c)
  idx := (c as i32) - 97
  counts[idx] <- counts[idx] + 1

Early Return from Loop

return exits the enclosing function, not just the loop:

find :: fn(buf: []u8, target: u8) -> i32
  for x, i in buf
    return i as i32 if x == target
  -1

Return

return exits the function early with a value:

fn clamp(x: i32, lo: i32, hi: i32) -> i32
  return lo if x < lo
  return hi if x > hi
  x

In error union functions, return error.X wraps the error:

fn read_file(path: []const u8) -> !i32
  let fd = open(path.ptr, 0)
  return error.FileNotFound if fd < 0
  // ...