# if, while, for, switch and Labelled Blocks — Zig

Source: https://www.geekswithgeeks.com/en/zig/c-control

> 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.](assets/figures/zig/section-2-map.svg) — Figure 2.1 — Control flow that returns values.

## Control flow in practice

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

```zig
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.

**Quiz:** What does `for (items, 0..) |item, i|` do?

- [ ] Iterates items twice
- [ ] Declares a range of zeros
- [x] 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.
