enum

enum

enums.zig

const expect = @import("std").testing.expect;
const mem = @import("std").mem;

// Declare an enum.
const Type = enum {
    ok,
    not_ok,
};

// Declare a specific instance of the enum variant.
const c = Type.ok;

// If you want access to the ordinal value of an enum, you
// can specify the tag type.
const Value = enum(u2) {
    zero,
    one,
    two,
};

// Now you can cast between u2 and Value.
// The ordinal value starts from 0, counting up for each member.
test "enum ordinal value" {
    try expect(@enumToInt(Value.zero) == 0);
    try expect(@enumToInt(Value.one) == 1);
    try expect(@enumToInt(Value.two) == 2);
}

// You can override the ordinal value for an enum.
const Value2 = enum(u32) {
    hundred = 100,
    thousand = 1000,
    million = 1000000,
};
test "set enum ordinal value" {
    try expect(@enumToInt(Value2.hundred) == 100);
    try expect(@enumToInt(Value2.thousand) == 1000);
    try expect(@enumToInt(Value2.million) == 1000000);
}

// Enums can have methods, the same as structs and unions.
// Enum methods are not special, they are only namespaced
// functions that you can call with dot syntax.
const Suit = enum {
    clubs,
    spades,
    diamonds,
    hearts,

    pub fn isClubs(self: Suit) bool {
        return self == Suit.clubs;
    }
};
test "enum method" {
    const p = Suit.spades;
    try expect(!p.isClubs());
}

// An enum variant of different types can be switched upon.
const Foo = enum {
    string,
    number,
    none,
};
test "enum variant switch" {
    const p = Foo.number;
    const what_is_it = switch (p) {
        Foo.string => "this is a string",
        Foo.number => "this is a number",
        Foo.none => "this is a none",
    };
    try expect(mem.eql(u8, what_is_it, "this is a number"));
}

// @typeInfo can be used to access the integer tag type of an enum.
const Small = enum {
    one,
    two,
    three,
    four,
};
test "std.meta.Tag" {
    try expect(@typeInfo(Small).Enum.tag_type == u2);
}

// @typeInfo tells us the field count and the fields names:
test "@typeInfo" {
    try expect(@typeInfo(Small).Enum.fields.len == 4);
    try expect(mem.eql(u8, @typeInfo(Small).Enum.fields[1].name, "two"));
}

// @tagName gives a [:0]const u8 representation of an enum value:
test "@tagName" {
    try expect(mem.eql(u8, @tagName(Small.three), "three"));
}

Shell

$ zig test enums.zig
1/7 test "enum ordinal value"... OK
2/7 test "set enum ordinal value"... OK
3/7 test "enum method"... OK
4/7 test "enum variant switch"... OK
5/7 test "std.meta.Tag"... OK
6/7 test "@typeInfo"... OK
7/7 test "@tagName"... OK
All 7 tests passed.

See also:

  • @typeInfo
  • @tagName
  • @sizeOf

extern enum

By default, enums are not guaranteed to be compatible with the C ABI:

test.zig

const Foo = enum { a, b, c };
export fn entry(foo: Foo) void { _ = foo; }

Shell

$ zig build-obj test.zig
./docgen_tmp/test.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'
export fn entry(foo: Foo) void { _ = foo; }
                     ^

For a C-ABI-compatible enum, provide an explicit tag type to the enum:

test.zig

const Foo = enum(c_int) { a, b, c };
export fn entry(foo: Foo) void { _ = foo; }

Shell

$ zig build-obj test.zig

Enum Literals

Enum literals allow specifying the name of an enum field without specifying the enum type:

test_enum_literals.zig

const std = @import("std");
const expect = std.testing.expect;

const Color = enum {
    auto,
    off,
    on,
};

test "enum literals" {
    const color1: Color = .auto;
    const color2 = Color.auto;
    try expect(color1 == color2);
}

test "switch using enum literals" {
    const color = Color.on;
    const result = switch (color) {
        .auto => false,
        .on => true,
        .off => false,
    };
    try expect(result);
}

Shell

$ zig test test_enum_literals.zig
1/2 test "enum literals"... OK
2/2 test "switch using enum literals"... OK
All 2 tests passed.

Non-exhaustive enum

A Non-exhaustive enum can be created by adding a trailing ‘_‘ field. It must specify a tag type and cannot consume every enumeration value.

@intToEnum on a non-exhaustive enum involves the safety semantics of @intCast to the integer tag type, but beyond that always results in a well-defined enum value.

A switch on a non-exhaustive enum can include a ‘_‘ prong as an alternative to an else prong with the difference being that it makes it a compile error if all the known tag names are not handled by the switch.

test_switch_non-exhaustive.zig

const std = @import("std");
const expect = std.testing.expect;

const Number = enum(u8) {
    one,
    two,
    three,
    _,
};

test "switch on non-exhaustive enum" {
    const number = Number.one;
    const result = switch (number) {
        .one => true,
        .two,
        .three => false,
        _ => false,
    };
    try expect(result);
    const is_one = switch (number) {
        .one => true,
        else => false,
    };
    try expect(is_one);
}

Shell

$ zig test test_switch_non-exhaustive.zig
1/1 test "switch on non-exhaustive enum"... OK
All 1 tests passed.