Skip to content

`for` Loops

A for loop visits the elements of an array, slice, or range.

for Loops

A for loop visits the elements of an array, slice, or range.

const std = @import("std");

pub fn main() void {
    const data = [_]u8{ 10, 20, 30 };

    for (data) |x| {
        std.debug.print("{d}\n", .{x});
    }
}

The output is:

10
20
30

The name between bars is the loop capture.

|x|

On each iteration, x is the current element.

A for loop over an array visits each element in order.

const letters = [_]u8{ 'a', 'b', 'c' };

for (letters) |ch| {
    std.debug.print("{c}\n", .{ch});
}

A string literal is an array of bytes, so a loop over a string visits bytes.

for ("zig") |b| {
    std.debug.print("{c}\n", .{b});
}

This prints:

z
i
g

This is byte iteration. It is not Unicode character iteration. A UTF-8 character may use more than one byte.

A for loop can also capture the index.

const data = [_]u8{ 10, 20, 30 };

for (data, 0..) |x, i| {
    std.debug.print("{d}: {d}\n", .{ i, x });
}

The output is:

0: 10
1: 20
2: 30

The expression 0.. is an open-ended range. In this loop, Zig pairs it with data, so it gives the indices 0, 1, and 2.

A closed range has both ends.

for (0..5) |i| {
    std.debug.print("{d}\n", .{i});
}

This prints:

0
1
2
3
4

The end is not included.

A for loop can iterate over more than one sequence at the same time.

const names = [_][]const u8{ "red", "green", "blue" };
const values = [_]u8{ 1, 2, 3 };

for (names, values) |name, value| {
    std.debug.print("{s}: {d}\n", .{ name, value });
}

All sequences must have the same length, unless one of them is an open-ended range used for indexing.

Use a pointer capture to modify elements.

var data = [_]u8{ 1, 2, 3 };

for (&data) |*x| {
    x.* += 1;
}

After the loop, data is:

[_]u8{ 2, 3, 4 }

The &data passes a pointer to the array. The capture |*x| captures a pointer to each element. The expression x.* means the value pointed to by x.

Without pointer capture, the loop gets a copy of each element.

var data = [_]u8{ 1, 2, 3 };

for (data) |x| {
    _ = x + 1;
}

This does not change data.

Use break to leave a for loop.

const data = [_]u8{ 5, 8, 13, 21 };

for (data) |x| {
    if (x == 13) break;
    std.debug.print("{d}\n", .{x});
}

This prints:

5
8

Use continue to skip one iteration.

const data = [_]u8{ 1, 2, 3, 4, 5 };

for (data) |x| {
    if (x % 2 == 0) continue;
    std.debug.print("{d}\n", .{x});
}

This prints:

1
3
5

Like while, a for loop may have an else branch. The else branch runs only if the loop finishes normally.

const data = [_]u8{ 1, 2, 3 };

for (data) |x| {
    if (x == 9) break;
} else {
    std.debug.print("not found\n", .{});
}

The loop does not break, so the else branch runs.

A for loop can produce a value.

const data = [_]u8{ 4, 7, 9, 12 };

const found = for (data, 0..) |x, i| {
    if (x == 9) break i;
} else null;

The value of found is 2. If no element equals 9, the value is null.

This is a compact search pattern.

const index = for (data, 0..) |x, i| {
    if (x == target) break i;
} else null;

The type of index is ?usize.

Use for when the number of iterations comes from the data. Use while when the loop is controlled by a condition that changes in a less regular way.

for (items) |item| {
    use(item);
}

This says: visit every item.

while (readNext()) |item| {
    use(item);
}

This says: keep going while another item can be read.

Exercise 3-17. Write a for loop that prints every element in an array.

Exercise 3-18. Print each element with its index.

Exercise 3-19. Use pointer capture to double every number in an array.

Exercise 3-20. Write a for expression that returns the index of the first even number, or null if there is none.