enum

enums.zig

  1. const expect = @import("std").testing.expect;
  2. const mem = @import("std").mem;
  3. // Declare an enum.
  4. const Type = enum {
  5. ok,
  6. not_ok,
  7. };
  8. // Declare a specific instance of the enum variant.
  9. const c = Type.ok;
  10. // If you want access to the ordinal value of an enum, you
  11. // can specify the tag type.
  12. const Value = enum(u2) {
  13. zero,
  14. one,
  15. two,
  16. };
  17. // Now you can cast between u2 and Value.
  18. // The ordinal value starts from 0, counting up for each member.
  19. test "enum ordinal value" {
  20. try expect(@enumToInt(Value.zero) == 0);
  21. try expect(@enumToInt(Value.one) == 1);
  22. try expect(@enumToInt(Value.two) == 2);
  23. }
  24. // You can override the ordinal value for an enum.
  25. const Value2 = enum(u32) {
  26. hundred = 100,
  27. thousand = 1000,
  28. million = 1000000,
  29. };
  30. test "set enum ordinal value" {
  31. try expect(@enumToInt(Value2.hundred) == 100);
  32. try expect(@enumToInt(Value2.thousand) == 1000);
  33. try expect(@enumToInt(Value2.million) == 1000000);
  34. }
  35. // Enums can have methods, the same as structs and unions.
  36. // Enum methods are not special, they are only namespaced
  37. // functions that you can call with dot syntax.
  38. const Suit = enum {
  39. clubs,
  40. spades,
  41. diamonds,
  42. hearts,
  43. pub fn isClubs(self: Suit) bool {
  44. return self == Suit.clubs;
  45. }
  46. };
  47. test "enum method" {
  48. const p = Suit.spades;
  49. try expect(!p.isClubs());
  50. }
  51. // An enum variant of different types can be switched upon.
  52. const Foo = enum {
  53. string,
  54. number,
  55. none,
  56. };
  57. test "enum variant switch" {
  58. const p = Foo.number;
  59. const what_is_it = switch (p) {
  60. Foo.string => "this is a string",
  61. Foo.number => "this is a number",
  62. Foo.none => "this is a none",
  63. };
  64. try expect(mem.eql(u8, what_is_it, "this is a number"));
  65. }
  66. // @typeInfo can be used to access the integer tag type of an enum.
  67. const Small = enum {
  68. one,
  69. two,
  70. three,
  71. four,
  72. };
  73. test "std.meta.Tag" {
  74. try expect(@typeInfo(Small).Enum.tag_type == u2);
  75. }
  76. // @typeInfo tells us the field count and the fields names:
  77. test "@typeInfo" {
  78. try expect(@typeInfo(Small).Enum.fields.len == 4);
  79. try expect(mem.eql(u8, @typeInfo(Small).Enum.fields[1].name, "two"));
  80. }
  81. // @tagName gives a [:0]const u8 representation of an enum value:
  82. test "@tagName" {
  83. try expect(mem.eql(u8, @tagName(Small.three), "three"));
  84. }

Shell

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

See also:

extern enum

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

test.zig

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

Shell

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

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

test.zig

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

Shell

  1. $ 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

  1. const std = @import("std");
  2. const expect = std.testing.expect;
  3. const Color = enum {
  4. auto,
  5. off,
  6. on,
  7. };
  8. test "enum literals" {
  9. const color1: Color = .auto;
  10. const color2 = Color.auto;
  11. try expect(color1 == color2);
  12. }
  13. test "switch using enum literals" {
  14. const color = Color.on;
  15. const result = switch (color) {
  16. .auto => false,
  17. .on => true,
  18. .off => false,
  19. };
  20. try expect(result);
  21. }

Shell

  1. $ zig test test_enum_literals.zig
  2. 1/2 test.enum literals... OK
  3. 2/2 test.switch using enum literals... OK
  4. 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

  1. const std = @import("std");
  2. const expect = std.testing.expect;
  3. const Number = enum(u8) {
  4. one,
  5. two,
  6. three,
  7. _,
  8. };
  9. test "switch on non-exhaustive enum" {
  10. const number = Number.one;
  11. const result = switch (number) {
  12. .one => true,
  13. .two,
  14. .three => false,
  15. _ => false,
  16. };
  17. try expect(result);
  18. const is_one = switch (number) {
  19. .one => true,
  20. else => false,
  21. };
  22. try expect(is_one);
  23. }

Shell

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