⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

Enums

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

const Direction = enum { north, south, east, west };

// The tag type can be pinned, which fixes the numeric values.
const Value = enum(u8) { zero = 0, one = 1, hundred = 100 };

const Suit = enum {
    clubs,
    spades,
    diamonds,
    hearts,

    // Enums can have methods; they are namespaced functions, not vtables.
    pub fn isRed(self: Suit) bool {
        return switch (self) {
            .diamonds, .hearts => true,
            .clubs, .spades => false,
        };
    }
};

test "enum values" {
    try expect(@intFromEnum(Value.zero) == 0);
    try expect(@intFromEnum(Value.hundred) == 100);
}

test "inferred enum literals" {
    // When the type is known, `.north` is enough.
    const d: Direction = .north;
    try expect(d == Direction.north);
}

test "enum methods" {
    try expect(Suit.hearts.isRed());
    try expect(!Suit.spades.isRed());
}

test "enums expose their tags at comptime" {
    const info = @typeInfo(Direction).@"enum";

    // Names and values are parallel arrays, not one array of field structs.
    try expect(info.field_names.len == 4);
    try expect(info.field_values.len == info.field_names.len);
    try expect(std.mem.eql(u8, info.field_names[2], "east"));

    try expect(info.tag_type == u2);
    try expect(info.mode == .exhaustive);

    try expect(std.mem.eql(u8, @tagName(Direction.east), "east"));
}

Inferred literals

When the expected type is known, the leading dot is enough:

const d: Direction = .north;
takeDirection(.south);

This keeps switch prongs and struct literals readable without repeating the type name everywhere.

Pinning the representation

const Value = enum(u8) { zero = 0, one = 1, hundred = 100 };

Specifying the tag type fixes both the size and the numeric values, which matters for wire formats and C interop. Convert with @intFromEnum and @enumFromInt. The latter is checked in safety builds, because not every integer is a valid tag.

Methods

Enums can declare functions. Suit.hearts.isRed() is exactly Suit.isRed(Suit.hearts): method syntax is sugar for passing the receiver as the first argument. There is no dynamic dispatch and no vtable.

Reflection

@typeInfo(T).@"enum" exposes the tags at comptime:

const info = @typeInfo(Direction).@"enum";
info.field_names[2];   // "east"
info.field_values[2];  // 2
info.tag_type;         // u2
info.mode;             // .exhaustive

Note these are parallel arrays, not one array of field structs. Older tutorials show info.fields[i].name; that shape is gone. The split landed between 0.17.0-dev.644 and dev.1441, and was the only break across all 53 snippets in this guide when the compiler was updated.

@tagName gives the name as a string. Together this is how generic code prints or parses enums without a hand-written table.