while.zig

    1. 1/1 test "while basic"... OK
    2. All 1 tests passed.

    Use break to exit a while loop early.

    while.zig

    1. const expect = @import("std").testing.expect;
    2. test "while break" {
    3. var i: usize = 0;
    4. while (true) {
    5. if (i == 10)
    6. break;
    7. i += 1;
    8. }
    9. expect(i == 10);
    10. }
    1. $ zig test while.zig
    2. 1/1 test "while break"... OK
    3. All 1 tests passed.

    Use continue to jump back to the beginning of the loop.

    while.zig

    1. const expect = @import("std").testing.expect;
    2. test "while continue" {
    3. var i: usize = 0;
    4. while (true) {
    5. i += 1;
    6. if (i < 10)
    7. continue;
    8. break;
    9. }
    10. expect(i == 10);
    11. }
    1. $ zig test while.zig
    2. 1/1 test "while continue"... OK
    3. All 1 tests passed.

    While loops support a continue expression which is executed when the loop is continued. The continue keyword respects this expression.

    while.zig

    1. $ zig test while.zig
    2. 1/2 test "while loop continue expression"... OK
    3. 2/2 test "while loop continue expression, more complicated"... OK
    4. All 2 tests passed.

    break, like return, accepts a value parameter. This is the result of the while expression. When you break from a while loop, the else branch is not evaluated.

    while.zig

    1. const expect = @import("std").testing.expect;
    2. expect(rangeHasNumber(0, 10, 5));
    3. }
    4. fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
    5. var i = begin;
    6. return while (i < end) : (i += 1) {
    7. if (i == number) {
    8. break true;
    9. }
    10. } else false;
    11. }
    1. $ zig test while.zig
    2. 1/1 test "while else"... OK
    3. All 1 tests passed.

    When a while loop is labeled, it can be referenced from a break or continue from within a nested loop:

    test.zig

    1. test "nested break" {
    2. outer: while (true) {
    3. while (true) {
    4. break :outer;
    5. }
    6. }
    7. }
    8. test "nested continue" {
    9. var i: usize = 0;
    10. outer: while (i < 10) : (i += 1) {
    11. while (true) {
    12. continue :outer;
    13. }
    14. }
    15. }
    1. $ zig test test.zig
    2. 1/2 test "nested break"... OK
    3. 2/2 test "nested continue"... OK
    4. All 2 tests passed.

    Just like if expressions, while loops can take an optional as the condition and capture the payload. When is encountered the loop exits.

    When the |x| syntax is present on a while expression, the while condition must have an Optional Type.

    The else branch is allowed on optional iteration. In this case, it will be executed on the first null value encountered.

    1. $ zig test while.zig
    2. 1/1 test "while null capture"... OK
    3. All 1 tests passed.

    Just like expressions, while loops can take an error union as the condition and capture the payload or the error code. When the condition results in an error code the else branch is evaluated and the loop is finished.

    When the else |x| syntax is present on a while expression, the while condition must have an Error Union Type.

    while.zig

    1. test "while error union capture" {
    2. var sum1: u32 = 0;
    3. numbers_left = 3;
    4. while (eventuallyErrorSequence()) |value| {
    5. sum1 += value;
    6. } else |err| {
    7. }
    8. }
    9. var numbers_left: u32 = undefined;
    10. fn eventuallyErrorSequence() anyerror!u32 {
    11. return if (numbers_left == 0) error.ReachedZero else blk: {
    12. numbers_left -= 1;
    13. break :blk numbers_left;
    14. };
    15. }
    1. $ zig test while.zig
    2. 1/1 test "while error union capture"... OK
    3. All 1 tests passed.

    While 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.

    test.zig

    1. const expect = @import("std").testing.expect;
    2. test "inline while loop" {
    3. comptime var i = 0;
    4. var sum: usize = 0;
    5. inline while (i < 3) : (i += 1) {
    6. const T = switch (i) {
    7. 0 => f32,
    8. 1 => i8,
    9. 2 => bool,
    10. else => unreachable,
    11. };
    12. sum += typeNameLength(T);
    13. }
    14. expect(sum == 9);
    15. }
    16. fn typeNameLength(comptime T: type) usize {
    17. return @typeName(T).len;
    18. }
    1. $ zig test test.zig
    2. 1/1 test "inline while loop"... OK
    3. All 1 tests passed.

    It is recommended to use inline loops only for one of these reasons:

    • You need the loop to execute at for the semantics to work.
    • You have a benchmark to prove that forcibly unrolling the loop in this way is measurably faster.

    See also: