Zero Bit Types

    • The Integers and i0.
    • and Vectors with len 0, or with an element type that is a zero bit type.
    • An with only 1 tag.
    • A struct with all fields being zero bit types.
    • A with only 1 field which is a zero bit type.
    • Pointers to Zero Bit Types are themselves zero bit types.

    These types can only ever have one possible value, and thus require 0 bits to represent. Code that makes use of these types is not included in the final generated code:

    test.zig

    When this turns into machine code, there is no code generated in the body of entry, even in mode. For example, on x86_64:

    1. 0000000000000010 <entry>:
    2. 10: 55 push %rbp
    3. 11: 48 89 e5 mov %rsp,%rbp
    4. 14: 5d pop %rbp
    5. 15: c3 retq

    These assembly instructions do not have any code associated with the void values - they only perform the function call prologue and epilog.

    void can be useful for instantiating generic types. For example, given a Map(Key, Value), one can pass void for the Value type to make it into a Set:

    void_in_hashmap.zig

    1. const std = @import("std");
    2. const expect = std.testing.expect;
    3. test "turn HashMap into a set with void" {
    4. var map = std.AutoHashMap(i32, void).init(std.testing.allocator);
    5. defer map.deinit();
    6. try map.put(1, {});
    7. try map.put(2, {});
    8. try expect(map.contains(2));
    9. try expect(!map.contains(3));
    10. try expect(!map.contains(2));
    11. }
    1. $ zig test void_in_hashmap.zig
    2. All 1 tests passed.

    Note that this is different from using a dummy value for the hash map value. By using void as the type of the value, the hash map entry type has no value field, and thus the hash map takes up less space. Further, all the code that deals with storing and loading the value is deleted, as seen above.

    void is distinct from c_void. void has a known size of 0 bytes, and c_void has an unknown, but non-zero, size.

    Expressions of type void are the only ones whose value can be ignored. For example:

    test.zig

    Shell

    1. $ zig test test.zig
    2. ./docgen_tmp/test.zig:2:8: error: expression value is ignored
    3. foo();
    4. ^
    5. ./docgen_tmp/test.zig:1:34: note: referenced here
    6. test "ignoring expression value" {
    7. ^

    However, if the expression has type void, there will be no error. Function return values can also be explicitly ignored by assigning them to _.

    1. test "void is ignored" {
    2. returnsVoid();
    3. }
    4. test "explicitly ignoring expression value" {
    5. _ = foo();
    6. }
    7. fn returnsVoid() void {}
    8. fn foo() i32 {
    9. return 1234;
    10. }

    Shell

    1. 1/2 test "void is ignored"... OK
    2. All 2 tests passed.

    Pointers to zero bit types also have zero bits. They always compare equal to each other:

    pointers_to_zero_bits.zig

    Shell

    1. $ zig test pointers_to_zero_bits.zig
    2. 1/1 test "pointer to empty struct"... OK
    3. All 1 tests passed.

    The type being pointed to can only ever be one value; therefore loads and stores are never generated. ptrToInt and are not allowed:

    test.zig

    1. const Empty = struct {};
    2. test "@ptrToInt for pointer to zero bit type" {
    3. var a = Empty{};
    4. _ = @ptrToInt(&a);
    5. }
    6. test "@intToPtr for pointer to zero bit type" {
    7. _ = @intToPtr(*Empty, 0x1);
    8. }
    1. $ zig test test.zig
    2. ./docgen_tmp/test.zig:4:5: error: pointer to size 0 type has no address
    3. var a = Empty{};
    4. ^
    5. ./docgen_tmp/test.zig:5:9: note: referenced here
    6. _ = @ptrToInt(&a);
    7. ^
    8. ./docgen_tmp/test.zig:9:19: error: type '*Empty' has 0 bits and cannot store information
    9. _ = @intToPtr(*Empty, 0x1);
    10. ^
    11. ./docgen_tmp/test.zig:9:9: note: referenced here
    12. _ = @intToPtr(*Empty, 0x1);
    13. ^