null 언랩 시도

null 언랩 시도

옵셔널을 ? 연산자로 바로 풀었는데, 그 옵셔널이 실제로 null을 들고 있으면 어떻게 될까요? Zig는 조용히 넘어가지 않고 컴파일 타임이든 런타임이든 분명하게 실패시켜요. 이번 섹션에서는 그 예외 상황을 하나씩 살펴볼게요.

출처: Zig Documentation

본문

컴파일 타임에는

comptime {
    const optional_number: ?i32 = null;
    const number = optional_number.?;
    _ = number;
}

Shell:

$ zig test test_comptime_unwrap_null.zig
/home/ci/work/zig-bootstrap/zig/doc/langref/test_comptime_unwrap_null.zig:3:35: error: unable to unwrap null
    const number = optional_number.?;
                   ~~~~~~~~~~~~~~~^~

컴파일 타임 연산은 값이 다 정해져 있으니, 컴파일러가 **"unable to unwrap null"**이라는 오류를 내면서 빌드를 그 자리에서 막아요. ?는 '값이 들어 있다고 보고, null은 절대 아니다'고 약속하는 거라서, 그 약속이 깨지는 순간 컴파일 오류가 되는 거예요.

런타임에는

const std = @import("std");

pub fn main() void {
    var optional_number: ?i32 = null;
    _ = &optional_number;
    const number = optional_number.?;
    std.debug.print("value: {}\n", .{number});
}

Shell:

$ zig build-exe runtime_unwrap_null.zig
$ ./runtime_unwrap_null
thread 973907 panic: attempt to use null value
/home/ci/work/zig-bootstrap/zig/doc/langref/runtime_unwrap_null.zig:6:35: 0x11e82b4 in main (runtime_unwrap_null.zig)
    const number = optional_number.?;
                                  ^
/home/ci/work/zig-bootstrap/out/host/lib/zig/std/start.zig:788:64: 0x11e7bbb in callMain (std.zig)
    if (fn_info.param_types.len == 0) return wrapMain(root.main());
                                                               ^
/home/ci/work/zig-bootstrap/out/host/lib/zig/std/start.zig:248:5: 0x11e75e1 in _start (std.zig)
    asm volatile (switch (native_arch) {
    ^
(process terminated by signal)

런타임에는 컴파일러가 미리 알 수 없으니, null이 실제로 도착하는 순간 **"attempt to use null value"**라는 메시지와 함께 패닉이 나고 프로세스가 종료돼요. 잘못된 값을 조용히 넘겨서 프로그램을 계속 돌리는 대신, 문제가 난 위치를 스택 트레이스로 남겨서 바로 찾게 해 주는 거죠.

if로 null을 미리 확인하기

이런 충돌을 피하는 한 가지 방법은 null이 아닌 걸 전제하는 대신 if 표현식으로 null인지부터 확인하는 거예요.

const print = @import("std").debug.print;
pub fn main() void {
    const optional_number: ?i32 = null;

    if (optional_number) |number| {
        print("got number: {}\n", .{number});
    } else {
        print("it's null\n", .{});
    }
}

Shell:

$ zig build-exe testing_null_with_if.zig
$ ./testing_null_with_if
it's null

if (optional_number) |number|로 값이 들어 있으면 그 값을, elsenull이면 그 경로를 각각 받아서 처리해요. 이번에는 null이라서 it's null이 출력됐지만, 어떤 경우에도 프로그램이 터지지 않고 두 경로를 안전하게 나눠 처리할 수 있어요.

더 알아보기