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:
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:17: error: parameter of type 'test.Foo' not allowed in function with calling convention 'C'
export fn entry(foo: Foo) void { _ = foo; }
^~~~~~~~
docgen_tmp/test.zig:2:17: note: enum tag type 'u2' is not extern compatible
docgen_tmp/test.zig:2:17: note: only integers with power of two bits are extern compatible
docgen_tmp/test.zig:1:13: note: enum declared here
const Foo = enum { a, b, c };
^~~~~~~~~~~~~~~~
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.