for
for.zig
const assert = @import("std").debug.assert;
test "for basics" {
const items = []i32 { 4, 5, 3, 4, 0 };
var sum: i32 = 0;
// For loops iterate over slices and arrays.
for (items) |value| {
// Break and continue are supported.
if (value == 0) {
continue;
}
sum += value;
}
assert(sum == 16);
// To iterate over a portion of a slice, reslice.
for (items[0..1]) |value| {
sum += value;
}
assert(sum == 20);
// To access the index of iteration, specify a second capture value.
// This is zero-indexed.
var sum2: i32 = 0;
for (items) |value, i| {
assert(@typeOf(i) == usize);
sum2 += @intCast(i32, i);
}
assert(sum2 == 10);
}
test "for reference" {
var items = []i32 { 3, 4, 2 };
// Iterate over the slice by reference by
// specifying that the capture value is a pointer.
for (items) |*value| {
value.* += 1;
}
assert(items[0] == 4);
assert(items[1] == 5);
assert(items[2] == 3);
}
test "for else" {
// For allows an else attached to it, the same as a while loop.
var items = []?i32 { 3, 4, null, 5 };
// For loops can also be used as expressions.
var sum: i32 = 0;
const result = for (items) |value| {
if (value == null) {
break 9;
} else {
sum += value.?;
}
} else blk: {
assert(sum == 7);
break :blk sum;
};
}
$ zig test for.zig
Test 1/3 for basics...OK
Test 2/3 for reference...OK
Test 3/3 for else...OK
All tests passed.
Labeled for
When a for
loop is labeled, it can be referenced from a break
or continue
from within a nested loop:
test.zig
const std = @import("std");
const assert = std.debug.assert;
test "nested break" {
var count: usize = 0;
outer: for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
count += 1;
break :outer;
}
}
assert(count == 1);
}
test "nested continue" {
var count: usize = 0;
outer: for ([]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| {
for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
count += 1;
continue :outer;
}
}
assert(count == 8);
}
$ zig test test.zig
Test 1/2 nested break...OK
Test 2/2 nested continue...OK
All tests passed.
inline for
For loops can be inlined. This causes the loop to be unrolled, which allows the code to do some things which only work at compile time, such as use types as first class values. The capture value and iterator value of inlined for loops are compile-time known.
test.zig
const assert = @import("std").debug.assert;
test "inline for loop" {
const nums = []i32{2, 4, 6};
var sum: usize = 0;
inline for (nums) |i| {
const T = switch (i) {
2 => f32,
4 => i8,
6 => bool,
else => unreachable,
};
sum += typeNameLength(T);
}
assert(sum == 9);
}
fn typeNameLength(comptime T: type) usize {
return @typeName(T).len;
}
$ zig test test.zig
Test 1/1 inline for loop...OK
All tests passed.
It is recommended to use inline
loops only for one of these reasons:
- You need the loop to execute at comptime for the semantics to work.
- You have a benchmark to prove that forcibly unrolling the loop in this way is measurably faster.
See also: