enum

enums.zig

  1. const assert = @import("std").debug.assert;
  2. const mem = @import("std").mem;
  3. // Declare an enum.
  4. const Type = enum {
  5. Ok,
  6. NotOk,
  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. assert(@enumToInt(Value.Zero) == 0);
  21. assert(@enumToInt(Value.One) == 1);
  22. assert(@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. assert(@enumToInt(Value2.Hundred) == 100);
  32. assert(@enumToInt(Value2.Thousand) == 1000);
  33. assert(@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. assert(!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. assert(mem.eql(u8, what_is_it, "this is a number"));
  65. }
  66. // @TagType 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 "@TagType" {
  74. assert(@TagType(Small) == u2);
  75. }
  76. // @typeInfo tells us the field count and the fields names:
  77. test "@typeInfo" {
  78. assert(@typeInfo(Small).Enum.fields.len == 4);
  79. assert(mem.eql(u8, @typeInfo(Small).Enum.fields[1].name, "Two"));
  80. }
  81. // @tagName gives a []const u8 representation of an enum value:
  82. test "@tagName" {
  83. assert(mem.eql(u8, @tagName(Small.Three), "Three"));
  84. }
  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 "@TagType"...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 { }
  1. $ zig build-obj test.zig
  2. ./docgen_tmp/test.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C'
  3. export fn entry(foo: Foo) void {
  4. ^

For a C-ABI-compatible enum, use extern enum:

test.zig

  1. const Foo = extern enum { A, B, C };
  2. export fn entry(foo: Foo) void { }
  1. $ zig build-obj test.zig

packed enum

By default, the size of enums is not guaranteed.

packed enum causes the size of the enum to be the same as the size of the integer tag type of the enum:

test.zig

  1. const std = @import("std");
  2. test "packed enum" {
  3. const Number = packed enum(u8) {
  4. One,
  5. Two,
  6. Three,
  7. };
  8. std.debug.assert(@sizeOf(Number) == @sizeOf(u8));
  9. }
  1. $ zig test test.zig
  2. 1/1 test "packed enum"...OK
  3. All 1 tests passed.

This makes the enum eligible to be in a packed struct.

Enum Literals

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

test.zig

  1. const std = @import("std");
  2. const assert = std.debug.assert;
  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. assert(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. assert(result);
  21. }
  1. $ zig test test.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 cannot fail.

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.zig

  1. const std = @import("std");
  2. const assert = std.debug.assert;
  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. assert(result);
  18. const is_one = switch (number) {
  19. .One => true,
  20. else => false,
  21. };
  22. assert(is_one);
  23. }
  1. $ zig test test.zig
  2. 1/1 test "switch on non-exhaustive enum"...OK
  3. All 1 tests passed.