⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

For Loops

const std = @import("std");
const expect = std.testing.expect;

test "iterate values" {
    const string = [_]u8{ 'a', 'b', 'c' };
    var count: usize = 0;
    for (string) |character| {
        if (character == 'b') count += 1;
    }
    try expect(count == 1);
}

test "iterate with an index" {
    const string = [_]u8{ 'a', 'b', 'c' };
    var last_index: usize = 0;
    for (string, 0..) |_, index| {
        last_index = index;
    }
    try expect(last_index == 2);
}

test "ranges" {
    var sum: usize = 0;
    for (0..5) |i| sum += i;
    try expect(sum == 10);
}

test "iterate two sequences at once" {
    // Multi-object `for` requires equal lengths; a mismatch is checked.
    const names = [_][]const u8{ "a", "b" };
    const scores = [_]u8{ 1, 2 };
    var total: u8 = 0;
    for (names, scores) |name, score| {
        _ = name;
        total += score;
    }
    try expect(total == 3);
}

test "mutate through a pointer capture" {
    var numbers = [_]u8{ 1, 2, 3 };
    for (&numbers) |*n| n.* *= 2;
    try expect(numbers[2] == 6);
}

Zig’s for iterates over something: a slice, an array, a range. There is no three-clause for (i = 0; i < n; i++); that is what while is for. The result is that the common case carries no opportunity for an off-by-one.

Indices are opt-in

for (items, 0..) |item, index| { ... }

The 0.. is a second sequence to iterate in lockstep, not special syntax. You only pay for the index when you ask for it.

Iterating several sequences together

The same mechanism zips any number of sequences:

for (names, scores) |name, score| { ... }

Lengths must match. A mismatch is checked, so this cannot silently read past the end of the shorter one.

Mutating in place

Capturing by value gives you a copy. To modify the underlying elements, iterate over a pointer to the array and capture by pointer:

for (&numbers) |*n| n.* *= 2;