Zig While Loop
A while loop repeats a block of code as long as a condition stays true. When the condition becomes false, the loop stops. Zig's while loop handles the most common looping patterns cleanly, and it also serves as Zig's only general-purpose loop (unlike C, Zig has no traditional C-style for loop with init-condition-increment).
Basic While Loop
var count: u32 = 1;
while (count <= 5) {
std.debug.print("Count: {d}\n", .{count});
count += 1;
}
count = 1
|
[ 1 <= 5 ? ] → true → print "Count: 1" → count = 2
[ 2 <= 5 ? ] → true → print "Count: 2" → count = 3
[ 3 <= 5 ? ] → true → print "Count: 3" → count = 4
[ 4 <= 5 ? ] → true → print "Count: 4" → count = 5
[ 5 <= 5 ? ] → true → print "Count: 5" → count = 6
[ 6 <= 5 ? ] → false → loop ends
Output:
Count: 1 Count: 2 Count: 3 Count: 4 Count: 5
While with a Continue Expression
Zig's while loop accepts an optional continue expression — code that runs after every iteration, just before checking the condition again. This replaces the third part of a C-style for loop:
var i: u32 = 0;
while (i < 5) : (i += 1) {
std.debug.print("i = {d}\n", .{i});
}
Structure:
while (condition) : (continue_expression) {
body
}
Execution order per loop:
1. Check condition
2. Run body (if true)
3. Run continue expression
4. Go back to step 1
The continue expression runs even when you use continue to skip the rest of the body. This guarantees the loop variable always advances, preventing accidental infinite loops.
Infinite Loop with break
A while loop with true as its condition runs forever until a break statement exits it:
var attempts: u8 = 0;
while (true) {
attempts += 1;
std.debug.print("Attempt {d}\n", .{attempts});
if (attempts >= 3) {
std.debug.print("Max attempts reached.\n", .{});
break;
}
}
[always true]
|
attempt 1 → continue
attempt 2 → continue
attempt 3 → break → exit loop
This pattern is common for event loops, retry logic, and menus where the exit condition is complex and determined inside the loop body.
Continue — Skip an Iteration
continue jumps immediately to the continue expression (or back to the condition check if there is none), skipping the remaining code in the current iteration:
var n: u32 = 0;
while (n < 10) : (n += 1) {
if (n % 2 == 0) continue; // skip even numbers
std.debug.print("{d} is odd\n", .{n});
}
n = 0 → even → skip → n = 1 n = 1 → odd → print "1 is odd" → n = 2 n = 2 → even → skip → n = 3 n = 3 → odd → print "3 is odd" → n = 4 ... and so on
While with an Else Branch
Zig's while loop supports an else branch. This runs only when the loop condition becomes false naturally — not when the loop exits via break:
var x: u32 = 0;
const target: u32 = 5;
while (x < 10) : (x += 1) {
if (x == target) {
std.debug.print("Found target at {d}\n", .{x});
break;
}
} else {
std.debug.print("Target not found in range.\n", .{});
}
Loop runs: x=0,1,2,3,4 → x==5 → found → break → else does NOT run If target = 99: x=0..9 → condition fails → else runs → "Target not found"
This is Zig's clean alternative to setting a flag variable to track whether a loop found what it was looking for.
While with Optional Capture
When a function returns an optional value (a value that can be null), the while loop can keep iterating as long as the optional has a value:
// Simulated: reading items from a list one by one
var items = [_]?u32{10, 20, null, 30};
var idx: usize = 0;
while (idx < items.len) : (idx += 1) {
if (items[idx]) |val| {
std.debug.print("Item: {d}\n", .{val});
}
}
items: [ 10 | 20 | null | 30 ]
↓ ↓ ↓ ↓
print print skip print
Nested While Loops
var row: u8 = 1;
while (row <= 3) : (row += 1) {
var col: u8 = 1;
while (col <= 3) : (col += 1) {
std.debug.print("{d}x{d}={d} ", .{row, col, row * col});
}
std.debug.print("\n", .{});
}
Output (a 3x3 multiplication grid):
1x1=1 1x2=2 1x3=3 2x1=2 2x2=4 2x3=6 3x1=3 3x2=6 3x3=9
Breaking Out of Nested Loops with Labels
Zig lets you label a loop and break out of a specific outer loop from inside a nested one:
outer: while (true) {
var n: u32 = 0;
while (n < 5) : (n += 1) {
if (n == 3) break :outer; // exits the outer loop
std.debug.print("n={d}\n", .{n});
}
}
std.debug.print("Done.\n", .{});
n=0 → print n=1 → print n=2 → print n=3 → break :outer → jumps out of both loops → "Done."
Common Mistake: Forgetting to Advance the Loop Variable
WRONG: CORRECT:
var i: u32 = 0; var i: u32 = 0;
while (i < 5) { while (i < 5) : (i += 1) {
// forgot i += 1 std.debug.print("{d}\n", .{i});
// loops forever! }
}
Zig does not warn about infinite loops automatically. Always verify that your loop variable changes each iteration. Using the continue expression syntax : (i += 1) keeps the increment in one visible place and makes forgetting it much harder.
