Zero Bit Types
Zero Bit Types (0비트 타입)
어떤 타입은 값을 표현하는 데 비트가 전혀 필요 없어요. Zig에서는 이런 타입들에 대해 @sizeOf가 0이 됩니다. 이런 걸 '0비트 타입(Zero Bit Type)'이라고 불러요. 어떤 타입들이 그럴까요? 다음 목록을 보면 감이 올 거예요.
void- 정수 타입
u0 - 길이가 0이거나, 원소 타입이 0비트 타입인
배열(Array)과벡터(Vector) - 태그가 하나뿐인
enum - 모든 필드가 0비트 타입인
struct - 필드가 하나뿐이고 그 필드가 0비트 타입인
union
이런 타입들은 가질 수 있는 값이 단 하나뿐이라서, 표현하는 데 0비트면 충분해요. 그래서 이 타입을 다루는 코드는 최종 생성된 기계어에 아예 포함되지 않아요. 아래 예제를 볼게요.
export fn entry() void {
var x: void = {};
var y: void = {};
x = y;
y = x;
}
이것이 기계어로 변환되면, entry의 본문 안에는 Debug 모드에서조차 아무 코드도 생성되지 않아요. 예를 들어 x86_64에서 이렇게 됩니다.
0000000000000010 <entry>:
10: 55 push %rbp
11: 48 89 e5 mov %rsp,%rbp
14: 5d pop %rbp
15: c3 retq
이 어셈블리 명령들에는 void 값과 연관된 코드가 하나도 없어요. 그저 함수 호출의 프롤로그(prologue)와 에필로그(epilogue)만 수행할 뿐이죠.
본문
void
void는 제네릭 타입을 인스턴스화할 때 특히 유용해요. 예를 들어 Map(Key, Value)가 있다면, Value 자리에 void를 넘겨서 Set처럼 만들 수 있어요.
const std = @import("std");
const expect = std.testing.expect;
test "turn HashMap into a set with void" {
var map = std.AutoHashMap(i32, void).init(std.testing.allocator);
defer map.deinit();
try map.put(1, {});
try map.put(2, {});
try expect(map.contains(2));
try expect(!map.contains(3));
_ = map.remove(2);
try expect(!map.contains(2));
}
$ zig test test_void_in_hashmap.zig
1/1 test_void_in_hashmap.test.turn HashMap into a set with void...OK
All 1 tests passed.
여기서 중요한 건, 해시맵 값에 더미 값을 쓰는 것과는 다르다는 점이에요. 값의 타입을 void로 두면 해시맵 엔트리 타입에 값 필드가 생기지 않아서, 해시맵이 차지하는 공간이 줄어들어요. 게다가 값을 저장하고 불러오는 코드도 모두 삭제돼요 — 위에서 본 것처럼요.
한 가지 주의할 점은, void는 anyopaque와는 구별되는 타입이라는 거예요. void는 크기가 0바이트로 알려져 있지만, anyopaque는 크기가 0이 아닌 값인데 그 값이 알려지지 않았어요.
void 타입의 표현식은 그 값을 무시해도 되는 유일한 경우예요. 예를 들어, void가 아닌 표현식의 값을 무시하면 컴파일 에러가 납니다.
test "ignoring expression value" {
foo();
}
fn foo() i32 {
return 1234;
}
$ zig test test_expression_ignored.zig
/home/ci/work/zig-bootstrap/zig/doc/langref/test_expression_ignored.zig:2:8: error: value of type 'i32' ignored
foo();
~~~^~
/home/ci/work/zig-bootstrap/zig/doc/langref/test_expression_ignored.zig:2:8: note: all non-void values must be used
/home/ci/work/zig-bootstrap/zig/doc/langref/test_expression_ignored.zig:2:8: note: to discard the value, assign it to '_'
하지만 표현식의 타입이 void라면 아무 에러도 나지 않아요. 그리고 표현식의 결과는 _에 할당해서 명시적으로 무시할 수도 있어요.
test "void is ignored" {
returnsVoid();
}
test "explicitly ignoring expression value" {
_ = foo();
}
fn returnsVoid() void {}
fn foo() i32 {
return 1234;
}
$ zig test test_void_ignored.zig
1/2 test_void_ignored.test.void is ignored...OK
2/2 test_void_ignored.test.explicitly ignoring expression value...OK
All 2 tests passed.
더 알아보기 (Learn more)
앞으로 이어질 주제들이 궁금하다면 아래 링크를 따라가 보세요.
- Result Location Semantics — 함수 호출·구조체 리터럴의 결과가 놓이는 위치
- Primitive Types —
void를 포함한 기본 타입들의 개요 - Arrays · Vectors — 0비트 타입이 될 수 있었던 배열·벡터
- enum — 태그 하나뿐인 열거형
- struct · union — 0비트 필드로 구성되는 구조체·공용체
- @sizeOf — 타입의 바이트 크기를 구하는 내장 함수