Zig Enums
An enum (enumeration) defines a type that can hold exactly one value from a fixed set of named options. Instead of using raw numbers or strings to represent states like "pending," "active," and "closed," you define an enum with those names. The compiler then knows every valid value, catches typos, and forces you to handle every case in a switch.
Defining an Enum
const Direction = enum {
North,
South,
East,
West,
};
Direction can only be: ┌─────────┐ │ North │ │ South │ ← Pick exactly one │ East │ │ West │ └─────────┘
Using an Enum
const heading = Direction.North;
switch (heading) {
.North => std.debug.print("Going up on the map\n", .{}),
.South => std.debug.print("Going down on the map\n", .{}),
.East => std.debug.print("Going right on the map\n", .{}),
.West => std.debug.print("Going left on the map\n", .{}),
}
Inside a switch that already knows the type, you use the short dot syntax .North instead of writing the full Direction.North. The compiler fills in the type automatically.
Enum Integer Values
Every enum variant maps to an integer internally. By default Zig assigns 0, 1, 2, ... You can inspect or set these values explicitly:
const StatusCode = enum(u16) {
Ok = 200,
NotFound = 404,
ServerError = 500,
};
const code = StatusCode.NotFound;
const num = @intFromEnum(code);
std.debug.print("Code: {d}\n", .{num}); // 404
StatusCode: .Ok → 200 .NotFound → 404 .ServerError → 500 @intFromEnum(.NotFound) = 404 @enumFromInt(200) = .Ok
The backing integer type (u16) sets how large the number can be. Use the smallest type that fits your largest value.
Comparing Enum Values
const a = Direction.East;
const b = Direction.East;
const c = Direction.West;
std.debug.print("{}\n", .{a == b}); // true
std.debug.print("{}\n", .{a == c}); // false
Enums compare with == and !=. Each variant is a unique value — no two different variants are ever equal.
Enum Methods
Like structs, enums can have methods:
const Season = enum {
Spring,
Summer,
Autumn,
Winter,
fn isWarm(self: Season) bool {
return switch (self) {
.Spring, .Summer => true,
.Autumn, .Winter => false,
};
}
fn months(self: Season) []const u8 {
return switch (self) {
.Spring => "Mar–May",
.Summer => "Jun–Aug",
.Autumn => "Sep–Nov",
.Winter => "Dec–Feb",
};
}
};
const now = Season.Summer;
std.debug.print("Warm: {}\n", .{now.isWarm()}); // true
std.debug.print("Months: {s}\n", .{now.months()}); // Jun–Aug
Non-Exhaustive Enums
If you are wrapping a value from C or a protocol where new values might appear, mark the enum as non-exhaustive with _:
const KeyCode = enum(u32) {
Enter = 13,
Escape = 27,
Space = 32,
_, // allows values not listed above
};
A switch on a non-exhaustive enum requires an else branch for unrecognized values. Without it, the compiler reports an error.
Enum in Structs
const Priority = enum { Low, Medium, High, Critical };
const Task = struct {
title: []const u8,
priority: Priority,
done: bool = false,
};
const tasks = [_]Task{
.{ .title = "Fix login bug", .priority = .Critical },
.{ .title = "Update readme", .priority = .Low },
.{ .title = "Write tests", .priority = .High },
};
for (tasks) |task| {
std.debug.print("[{s}] {s}\n", .{
@tagName(task.priority),
task.title
});
}
Output: [Critical] Fix login bug [Low] Update readme [High] Write tests
@tagName converts an enum variant to its string name. This is useful for printing and logging enum values.
Iterating Over All Enum Values
const std = @import("std");
const Color = enum { Red, Green, Blue, Yellow };
pub fn main() void {
inline for (@typeInfo(Color).Enum.fields) |field| {
std.debug.print("Color: {s}\n", .{field.name});
}
}
Output:
Color: Red Color: Green Color: Blue Color: Yellow
@typeInfo returns compile-time information about a type. For enums, this includes the list of all fields. The inline for unrolls the loop at compile time, visiting each field as a compile-time constant.
Enum vs Constants
Constants: Enum:
+--------------------------+ +----------------------------+
| const NORTH = 0; | | const Direction = enum { |
| const SOUTH = 1; | | North, South, |
| const EAST = 2; | | East, West, |
| const WEST = 3; | | }; |
| | | |
| var dir: u8 = NORTH; | | var dir = Direction.North; |
| dir = 99; ← allowed! | | dir = Direction.North; |
| (silent bug) | | // dir = 99 → COMPILE ERR |
+--------------------------+ +----------------------------+
Constants let any integer value slip through. An enum restricts the value to exactly the defined variants. This restriction is a feature — it catches invalid states at compile time instead of causing mysterious bugs at runtime.
