Control Flow

7.1 if

if (x > 0) {
    std.debug.print("positive\n", .{});
} else if (x < 0) {
    std.debug.print("negative\n", .{});
} else {
    std.debug.print("zero\n", .{});
}

const abs = if (x < 0) -x else x;        // if as an expression (the ternary)

// if with optionals: unwrap with a payload capture
if (maybe_value) |v| {
    std.debug.print("got {d}\n", .{v});
} else {
    std.debug.print("null\n", .{});
}

// if with error unions
if (parse(input)) |n| {
    use(n);
} else |err| {
    std.debug.print("error: {}\n", .{err});
}

7.2 switch

const msg = switch (code) {
    200 => "OK",
    301, 302 => "Redirect",
    400...499 => "Client error",           // inclusive range
    500...599 => "Server error",
    else => "Unknown",
};

switch (shape) {                           // on a tagged union: payload capture
    .circle => |r| std.debug.print("circle {d}\n", .{r}),
    .rect => |dims| std.debug.print("{d}x{d}\n", .{ dims.w, dims.h }),
}

// switch must be exhaustive (enums/unions don't need else if all cases are covered)
// `inline else` generates a branch per case at comptime
switch (value) {
    inline else => |v, tag| std.debug.print("{s}: {any}\n", .{ @tagName(tag), v }),
}

7.3 Labeled Switch (0.14+): State Machines

This compiles to efficient jump tables, which is ideal for interpreters and lexers.

state: switch (State.start) {
    .start => continue :state .middle,     // jump directly to another prong
    .middle => if (done) continue :state .end else continue :state .middle,
    .end => {},
}

7.4 while Loops

var i: usize = 0;
while (i < 10) : (i += 1) {          // continue expression runs after each iteration
    if (i == 3) continue;
    if (i == 8) break;
}

while (iterator.next()) |item| {     // loop until null (optional payload)
    process(item);
}

while (reader.readByte()) |byte| {   // error-union loop
    _ = byte;
} else |err| {
    if (err != error.EndOfStream) return err;
}

const found = while (i < items.len) : (i += 1) {   // while as an expression
    if (items[i] == target) break true;
} else false;

7.5 for Loops

const items = [_]i32{ 10, 20, 30 };
for (items) |item| std.debug.print("{d}\n", .{item});
for (items, 0..) |item, idx| std.debug.print("{d}: {d}\n", .{ idx, item });   // with index
for (0..5) |n| std.debug.print("{d} ", .{n});                                  // range (exclusive end)
for (a, b) |x, y| std.debug.print("{d}\n", .{x + y});                          // multi-object (same length)

var arr = [_]i32{ 1, 2, 3 };
for (&arr) |*elem| elem.* *= 2;                                                // mutate via pointer capture

const has_neg = for (items) |v| {
    if (v < 0) break true;
} else false;

7.6 Labels, break, continue

outer: for (0..10) |i| {
    for (0..10) |j| {
        if (i * j == 42) break :outer;
        if (j > i) continue :outer;
    }
}

7.7 inline for / inline while

const types = .{ u8, u16, u32 };
inline for (types) |T| {                 // unrolled at compile time
    std.debug.print("{s}: {d} bytes\n", .{ @typeName(T), @sizeOf(T) });
}

7.8 unreachable

const val = switch (x % 2) {
    0 => "even",
    1 => "odd",
    else => unreachable,     // asserts this can't happen (panic in safe modes, UB in fast modes)
};