Zig For Loop

Zig's for loop iterates over arrays, slices, and ranges of integers. Unlike C's for loop which manages a counter manually, Zig's for loop directly visits each element or index in a collection. This design removes off-by-one errors and makes the intent of the loop immediately visible.

Iterating Over an Array

const fruits = [_][]const u8{ "Apple", "Mango", "Banana" };

for (fruits) |fruit| {
    std.debug.print("{s}\n", .{fruit});
}
  fruits array:
  [ "Apple" | "Mango" | "Banana" ]
       ↓          ↓         ↓
    fruit       fruit     fruit
    (iter 1)  (iter 2)  (iter 3)

  Output:
  Apple
  Mango
  Banana

The |fruit| syntax captures the current element. The loop visits every element from the first to the last automatically. You do not manage a counter or check bounds — Zig does it for you.

Iterating with an Index

When you need both the element and its position, add a second capture variable after a comma:

const scores = [_]u32{ 85, 92, 78, 95, 60 };

for (scores, 0..) |score, i| {
    std.debug.print("Student {d}: {d}\n", .{i + 1, score});
}
  scores: [ 85 | 92 | 78 | 95 | 60 ]
  index:     0    1    2    3    4

  i=0, score=85 → "Student 1: 85"
  i=1, score=92 → "Student 2: 92"
  i=2, score=78 → "Student 3: 78"
  i=3, score=95 → "Student 4: 95"
  i=4, score=60 → "Student 5: 60"

The 0.. after the array is a range that starts at zero and grows to match the array's length. The second capture i receives the current index as a usize.

Iterating Over an Integer Range

Use a range literal to loop a specific number of times:

for (1..6) |n| {
    std.debug.print("{d} squared = {d}\n", .{n, n * n});
}
  Range 1..6 produces: 1, 2, 3, 4, 5
  (The end value 6 is excluded — ranges are exclusive at the end)

  Output:
  1 squared = 1
  2 squared = 4
  3 squared = 9
  4 squared = 16
  5 squared = 25

The range 1..6 includes 1, 2, 3, 4, 5 — the end value (6) is not included. This matches the common pattern of iterating from start to end-exclusive, which makes calculating lengths straightforward.

For with break and continue

Both break and continue work inside for loops, just as with while loops:

const numbers = [_]i32{ 3, 7, -2, 8, -5, 4 };

for (numbers) |n| {
    if (n < 0) continue;   // skip negative numbers
    if (n > 7) break;      // stop if too large
    std.debug.print("{d}\n", .{n});
}
  n=3  → positive, not >7 → print 3
  n=7  → positive, not >7 → print 7
  n=-2 → negative → skip
  n=8  → >7 → break → loop ends

  Output:
  3
  7

For with an Else Branch

Like the while loop, a for loop runs its else branch when the loop finishes normally — not when it exits via break:

const data = [_]u32{ 10, 20, 30, 40 };
const target: u32 = 25;

for (data) |val| {
    if (val == target) {
        std.debug.print("Found {d}!\n", .{target});
        break;
    }
} else {
    std.debug.print("{d} not in list.\n", .{target});
}
  data: [ 10 | 20 | 30 | 40 ]
  target = 25

  10 ≠ 25 → 20 ≠ 25 → 30 ≠ 25 → 40 ≠ 25 → loop ends naturally
  → else runs → "25 not in list."

Iterating Over Multiple Arrays in Parallel

Zig's for loop can iterate over multiple arrays simultaneously, visiting corresponding elements from each:

const names = [_][]const u8{ "Riya", "Karan", "Anil" };
const marks = [_]u32{ 88, 95, 72 };

for (names, marks) |name, mark| {
    std.debug.print("{s}: {d}\n", .{name, mark});
}
  names: [ "Riya" | "Karan" | "Anil" ]
  marks: [   88   |   95    |   72   ]
               ↓        ↓        ↓
         Riya:88   Karan:95  Anil:72

Both arrays must have the same length. If they differ in size, Zig panics at runtime. This parallel iteration removes the need for manual index management when working with paired data.

Inline For Loop

Adding inline before for unrolls the loop at compile time. Each iteration runs as separate generated code. This is useful when the iteration count is small and known at compile time, and you want the compiler to optimize each iteration independently:

inline for (0..4) |i| {
    std.debug.print("Compile-time iteration {d}\n", .{i});
}

For vs While — Choosing the Right Loop

  Use FOR when:                  Use WHILE when:
  +------------------------+     +---------------------------+
  | You have a collection  |     | Condition is complex      |
  | (array, slice, range)  |     | You don't know how many   |
  | to visit every item    |     | iterations you need       |
  | Index access is needed |     | Reading until end of data |
  | Parallel iteration     |     | Retry/polling loops       |
  +------------------------+     +---------------------------+

  for (array) |item| {...}       while (not_done) {...}
  for (0..10) |i| {...}          while (true) {...}

Practical Example: Finding the Highest Score

const std = @import("std");

pub fn main() void {
    const scores = [_]u32{ 78, 95, 62, 88, 91, 55, 100, 73 };
    var highest: u32 = 0;
    var winner_idx: usize = 0;

    for (scores, 0..) |score, i| {
        if (score > highest) {
            highest = score;
            winner_idx = i;
        }
    }

    std.debug.print(
        "Highest score: {d} (Student {d})\n",
        .{highest, winner_idx + 1}
    );
}

Output:

Highest score: 100 (Student 7)

The for loop scans every score once, tracking the highest value seen and its position. No manual index arithmetic, no bounds checking — Zig handles both automatically. This is the natural way to scan collections in Zig.

Leave a Comment

Your email address will not be published. Required fields are marked *