옵셔널과 함께 쓰는 while

옵셔널과 함께 쓰는 while (while with Optionals)

while 루프도 if 표현식처럼 옵셔널을 조건으로 받아 그 값을 잡아낼(capture) 수 있어요. 조건이 null을 만나게 되면 루프는 거기서 종료됩니다.

출처: Zig Documentation

본문

옵셔널을 조건으로 쓰는 while은 간단해요. while 표현식에 |x| 구문이 붙어 있으면, 그 while의 조건은 반드시 **옵셔널 타입(Optional Type)**이어야 합니다. 조건이 null이 아니면 안에 담긴 값이 |value|로 묶여 루프 본문으로 들어오고, null이 되면 루프가 빠져나가죠.

옵셔널 순회에서는 else 분기도 쓸 수 있어요. 이 경우 else 블록은 처음으로 null을 만난 시점에 실행됩니다.

const expectEqual = @import("std").testing.expectEqual;

test "while null capture" {
    var sum1: u32 = 0;
    numbers_left = 3;
    while (eventuallyNullSequence()) |value| {
        sum1 += value;
    }
    try expectEqual(3, sum1);

    // null capture with an else block
    var sum2: u32 = 0;
    numbers_left = 3;
    while (eventuallyNullSequence()) |value| {
        sum2 += value;
    } else {
        try expectEqual(3, sum2);
    }

    // null capture with a continue expression
    var i: u32 = 0;
    var sum3: u32 = 0;
    numbers_left = 3;
    while (eventuallyNullSequence()) |value| : (i += 1) {
        sum3 += value;
    }
    try expectEqual(3, i);
}

var numbers_left: u32 = undefined;
fn eventuallyNullSequence() ?u32 {
    return if (numbers_left == 0) null else blk: {
        numbers_left -= 1;
        break :blk numbers_left;
    };
}
$ zig test test_while_null_capture.zig
1/1 test_while_null_capture.test.while null capture...OK
All 1 tests passed.

예제에서 eventuallyNullSequence()는 호출될 때마다 numbers_left를 하나씩 줄이며 그 값을 반환하다가, numbers_left0이 되면 null을 돌려줘요. 그래서 세 테스트 모두 처음 3번은 값이 잡히고 4번째 호출에서 루프가 끝나게 됩니다.

  • 첫 번째는 whileelse 없이 순수하게 |value| 캡처만 쓰는 기본 형태예요. 3번 반복하며 sum13이 되고, 다음 null에서 루프가 나가요.
  • 두 번째는 else 블록을 붙여서, null을 만난 순간(즉 값이 더 이상 없을 때) sum23인지 확인합니다.
  • 세 번째는 continue 표현식을 결합한 모습이에요. : (i += 1) 덕분에 루프가 반복될 때마다 카운터 i가 올라가고, null에서 종료된 뒤에도 i3으로 유지되는 걸 검증해요.

더 알아보기