for

for.zig

  1. const assert = @import("std").debug.assert;
  2. test "for basics" {
  3. const items = []i32 { 4, 5, 3, 4, 0 };
  4. var sum: i32 = 0;
  5. // For loops iterate over slices and arrays.
  6. for (items) |value| {
  7. // Break and continue are supported.
  8. if (value == 0) {
  9. continue;
  10. }
  11. sum += value;
  12. }
  13. assert(sum == 16);
  14. // To iterate over a portion of a slice, reslice.
  15. for (items[0..1]) |value| {
  16. sum += value;
  17. }
  18. assert(sum == 20);
  19. // To access the index of iteration, specify a second capture value.
  20. // This is zero-indexed.
  21. var sum2: i32 = 0;
  22. for (items) |value, i| {
  23. assert(@typeOf(i) == usize);
  24. sum2 += @intCast(i32, i);
  25. }
  26. assert(sum2 == 10);
  27. }
  28. test "for reference" {
  29. var items = []i32 { 3, 4, 2 };
  30. // Iterate over the slice by reference by
  31. // specifying that the capture value is a pointer.
  32. for (items) |*value| {
  33. value.* += 1;
  34. }
  35. assert(items[0] == 4);
  36. assert(items[1] == 5);
  37. assert(items[2] == 3);
  38. }
  39. test "for else" {
  40. // For allows an else attached to it, the same as a while loop.
  41. var items = []?i32 { 3, 4, null, 5 };
  42. // For loops can also be used as expressions.
  43. var sum: i32 = 0;
  44. const result = for (items) |value| {
  45. if (value == null) {
  46. break 9;
  47. } else {
  48. sum += value.?;
  49. }
  50. } else blk: {
  51. assert(sum == 7);
  52. break :blk sum;
  53. };
  54. }
  1. $ zig test for.zig
  2. Test 1/3 for basics...OK
  3. Test 2/3 for reference...OK
  4. Test 3/3 for else...OK
  5. 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

  1. const std = @import("std");
  2. const assert = std.debug.assert;
  3. test "nested break" {
  4. var count: usize = 0;
  5. outer: for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
  6. for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
  7. count += 1;
  8. break :outer;
  9. }
  10. }
  11. assert(count == 1);
  12. }
  13. test "nested continue" {
  14. var count: usize = 0;
  15. outer: for ([]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| {
  16. for ([]i32{ 1, 2, 3, 4, 5 }) |_| {
  17. count += 1;
  18. continue :outer;
  19. }
  20. }
  21. assert(count == 8);
  22. }
  1. $ zig test test.zig
  2. Test 1/2 nested break...OK
  3. Test 2/2 nested continue...OK
  4. 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

  1. const assert = @import("std").debug.assert;
  2. test "inline for loop" {
  3. const nums = []i32{2, 4, 6};
  4. var sum: usize = 0;
  5. inline for (nums) |i| {
  6. const T = switch (i) {
  7. 2 => f32,
  8. 4 => i8,
  9. 6 => bool,
  10. else => unreachable,
  11. };
  12. sum += typeNameLength(T);
  13. }
  14. assert(sum == 9);
  15. }
  16. fn typeNameLength(comptime T: type) usize {
  17. return @typeName(T).len;
  18. }
  1. $ zig test test.zig
  2. Test 1/1 inline for loop...OK
  3. 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: