पाठ 4 / 25

if, while, for, switch and Labelled Blocks

Use Zig's control-flow expressions, including captures and labelled breaks.

Control flow as expressions

if works as a statement or an expression (const fee = if (total >= 100_000) 0 else 4_900;) and can capture the payload of optionals and error unions: if (maybe_user) |user| { ... }. while loops can have a continue expression run after each iteration (while (i < n) : (i += 1)) and can also capture optional payloads, which is how iterators are consumed: while (it.next()) |item|. for loops iterate over arrays, slices and ranges, and since Zig 0.11 can iterate several objects in lockstep, including an index range: for (items, 0..) |item, i|. Capture by pointer (|*item|) to modify elements in place. Loops can have an else branch that runs when they end without break. switch must be exhaustive, supports ranges (1...9), multiple values, else, and captures for tagged unions. Labelled blocks turn a block into an expression: const x = blk: { ...; break :blk value; };, and labelled loops allow break :outer from nested loops. There is no goto and no implicit fallthrough.

Expressions everywhere

if, switch and labelled blocks produce values; loops can capture payloads.

Three flow shapes, a fork, a multi-way switch and a box with a labelled exit, each ending in an arrow that delivers a value into a variable slot.
Figure 2.1 — Control flow that returns values.

Control flow in practice

Expressions, captures, multi-object for and labelled blocks.

const std = @import("std");

fn shippingFee(total_paise: u64, express: bool) u64 {
    return if (total_paise >= 100_000) 0 else if (express) 9_900 else 4_900;
}

fn grade(marks: u8) u8 {
    return switch (marks) {
        85...100 => 'A',
        70...84 => 'B',
        40...69 => 'C',
        0...39 => 'F',
        else => '?',          // 101..255 are possible u8 values, so else is required
    };
}

pub fn main() void {
    const prices = [_]u32{ 4_950, 19_900, 9_900 };
    const qtys = [_]u32{ 2, 1, 5 };

    var total: u64 = 0;
    for (prices, qtys, 0..) |price, qty, i| {        // lockstep iteration with an index
        std.debug.print("line {d}: {d} x {d}\n", .{ i + 1, qty, price });
        total += @as(u64, price) * qty;
    }

    const first_expensive = blk: {
        for (prices, 0..) |p, i| {
            if (p > 10_000) break :blk i;
        }
        break :blk null;                         // block type is ?usize
    };

    var countdown: u8 = 3;
    while (countdown > 0) : (countdown -= 1) {
        std.debug.print("{d}...\n", .{countdown});
    }

    std.debug.print("total={d} fee={d} grade={c}\n", .{ total, shippingFee(total, false), grade(72) });
    if (first_expensive) |idx| std.debug.print("first item over Rs 100 at index {d}\n", .{idx});
}

switch on integers needs every value

A switch over a u8 must cover all 256 values, so add an else prong or narrow the type. Switching over an enum or tagged union without else gives you a compile error whenever a new case is added, which is exactly what you want.

त्वरित जाँच: What does `for (items, 0..) |item, i|` do?

  • Iterates items twice
  • Declares a range of zeros
  • Iterates items together with an index starting at 0
  • It is invalid syntax
Answer

Iterates items together with an index starting at 0 — Multi-object for loops iterate several sequences in lockstep, here items and an index range.