Cast Negative Number to Unsigned Integer

Cast Negative Number to Unsigned Integer

음수 값을 부호 없는 정수 타입으로 캐스팅하려고 할 때, 보통은 "@intCast로 그냥 넣으면 되지 않을까?" 싶어요. 그런데 Zig는 여기서 바로 걸려요. 컴파일 타임에 값을 알고 있으면 컴파일 단계에서, 런타임에 값을 알아야 하면 실행 중에 잡아냅니다. 두 경우를 하나씩 보여드릴게요.

출처: Zig Documentation

본문

먼저 컴파일 타임에 값이 정해져 있는 경우예요. -1u32로 바꾸려다가 에러가 나죠.

comptime {
    const value: i32 = -1;
    const unsigned: u32 = @intCast(value);
    _ = unsigned;
}
$ zig test test_comptime_invalid_cast.zig
/home/ci/work/zig-bootstrap/zig/doc/langref/test_comptime_invalid_cast.zig:3:36: error: type 'u32' cannot represent integer value '-1'
    const unsigned: u32 = @intCast(value);
                                   ^~~~~

@intCast는 값이 목표 타입에 들어갈 수 있는지 확인하는 캐스팅이에요. u32-1이라는 값을 표현할 수 없으니, 컴파일러가 "type 'u32' cannot represent integer value '-1'"이라고 정확히 알려줍니다.

이번엔 값이 런타임에 정해지는 경우예요. 컴파일 타임에는 값이 뭔지 몰라서 오류를 못 잡고, 프로그램을 실행했을 때 panic이 나옵니다.

const std = @import("std");

pub fn main() void {
    var value: i32 = -1; // runtime-known
    _ = &value;
    const unsigned: u32 = @intCast(value);
    std.debug.print("value: {}\n", .{unsigned});
}
$ zig build-exe runtime_invalid_cast.zig
$ ./runtime_invalid_cast
thread 975073 panic: integer does not fit in destination type
/home/ci/work/zig-bootstrap/zig/doc/langref/runtime_invalid_cast.zig:6:27: 0x11e829f in main (runtime_invalid_cast.zig)
    const unsigned: u32 = @intCast(value);
                          ^
/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)

var로 선언해서 값이 런타임에 결정되기 때문에, 컴파일러는 이 시점에 문제를 알 수 없어요. _ = &value;로 주소를 건드려줘야 값이 런타임 변수가 되는 점도 눈여겨볼 부분이에요. 실행하면 integer does not fit in destination type이라는 panic으로 끝납니다.

더 알아보기

만약 부호 없는 정수의 최댓값이 필요하다면, 직접 캐스팅하기보다 std.math.maxInt를 쓰는 게 안전해요.

const std = @import("std");
const max = std.math.maxInt(u32);

@intCast는 값의 범위를 지키는 캐스팅이라, 범위를 벗어나면 이 섹션에서 본 것처럼 컴파일 타임에는 컴파일 오류로, 런타임에는 panic으로 잡아줍니다.