네임스페이스 레벨 변수

네임스페이스 레벨 변수

Zig에서 변수를 선언하는 자리가 하나만 있는 게 아니에요. 함수 안의 지역 변수, 함수 밖의 네임스페이스 레벨 변수, 그리고 나중에 배울 스레드 지역 변수까지. 이번에는 그중 네임스페이스 레벨 변수(Namespace Level Variables) 가 어떤 성격을 갖는지 살펴볼게요.

출처: Zig Documentation

본문

네임스페이스 레벨 변수는 전역 수명(global lifetime) 을 가져요. 그리고 두 가지 특징이 더 있는데요, 선언 순서와 무관하고(order-independent), 지연 분석(lazily analyzed) 된다는 점이에요. 순서와 무관하다는 건, 쓰는 위치가 나중이라도 문제없다는 뜻이죠. 지연 분석은 실제로 사용될 때 분석이 이루어진다는 걸 의미해요.

네임스페이스 레벨 변수의 초기화 값은 암묵적으로 comptime이에요. 만약 그 변수가 const로 선언됐다면 값이 컴파일 타임에 알려지고(comptime-known), 그렇지 않다면 런타임에 알려지는(runtime-known) 값이 돼요.

아래 예시에서 x가 선언되기 전에 var yx를 참조하고 있는 걸 볼 수 있어요. 이렇게 순서를 뒤바꿔도 컴파일이 잘 되는 게, 네임스페이스 레벨 변수가 순서와 무관하기 때문이에요.

// test_namespace_level_variables.zig
var y: i32 = add(10, x);
const x: i32 = add(12, 34);

test "container level variables" {
    try expectEqual(46, x);
    try expectEqual(56, y);
}

fn add(a: i32, b: i32) i32 {
    return a + b;
}

const std = @import("std");
const expectEqual = std.testing.expectEqual;
$ zig test test_namespace_level_variables.zig
1/1 test_namespace_level_variables.test.container level variables...OK
All 1 tests passed.

네임스페이스 레벨 변수는 반드시 파일 최상위에만 둘 수 있는 건 아니에요. struct, union, enum, opaque 안에도 선언할 수 있어요.

아래 예시를 보면 S라는 struct 안에 var x가 들어 있고, foo()가 호출될 때마다 S.x를 하나씩 늘려가요. S.x += 1처럼 이름공간을 붙여 접근하는 걸 알 수 있죠.

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

test "namespaced container level variable" {
    try expectEqual(1235, foo());
    try expectEqual(1236, foo());
}

const S = struct {
    var x: i32 = 1234;
};

fn foo() i32 {
    S.x += 1;
    return S.x;
}
$ zig test test_namespaced_variable.zig
1/1 test_namespaced_variable.test.namespaced container level variable...OK
All 1 tests passed.

각 테스트 호출이 끝날 때마다 S.x가 1234 → 1235 → 1236으로 바뀌는 걸 보면, 네임스페이스 레벨 변수의 값이 호출 사이에 유지된다는 점도 함께 확인할 수 있어요. 함수 안의 지역 변수였다면 매 호출이 끝나면 초기화됐을 테니까요.

더 알아보기