An enum is a type whose values come from a fixed set of names.
const Color = enum {
red,
green,
blue,
};This declares a type named Color. It has three possible values: red, green, and blue.
A value is written with the type name:
const c = Color.red;When the expected type is known, the type name may be omitted:
const c: Color = .red;A complete program:
const std = @import("std");
const Color = enum {
red,
green,
blue,
};
pub fn main() void {
const c: Color = .green;
std.debug.print("{}\n", .{c});
}The output is:
.greenEnums work well with switch.
const std = @import("std");
const Color = enum {
red,
green,
blue,
};
pub fn main() void {
const c: Color = .blue;
const name = switch (c) {
.red => "red",
.green => "green",
.blue => "blue",
};
std.debug.print("{s}\n", .{name});
}The output is:
blueThe switch must cover all enum values.
If one case is missing, Zig reports an error at compile time.
const name = switch (c) {
.red => "red",
.green => "green",
};This is incomplete because .blue is not handled.
A switch may use else.
const name = switch (c) {
.red => "red",
else => "other",
};This compiles, but for small enums it is usually better to list every case. That way, adding a new enum value makes the compiler show every place that must be updated.
Enums may have an integer tag type.
const Status = enum(u8) {
ok = 0,
not_found = 1,
denied = 2,
};Here each enum value has a u8 integer representation.
The integer value can be obtained with @intFromEnum.
const n = @intFromEnum(Status.not_found);A complete example:
const std = @import("std");
const Status = enum(u8) {
ok = 0,
not_found = 1,
denied = 2,
};
pub fn main() void {
const s = Status.not_found;
const n = @intFromEnum(s);
std.debug.print("{d}\n", .{n});
}The output is:
1An integer may be converted back with @enumFromInt.
const s: Status = @enumFromInt(1);Use this only when the integer is known to be valid for the enum type. Unknown integer values are a safety problem.
Enums can contain declarations.
const Direction = enum {
north,
south,
east,
west,
pub fn isVertical(self: Direction) bool {
return switch (self) {
.north, .south => true,
.east, .west => false,
};
}
};The function belongs to the enum namespace.
const d: Direction = .north;
const yes = d.isVertical();A complete program:
const std = @import("std");
const Direction = enum {
north,
south,
east,
west,
pub fn isVertical(self: Direction) bool {
return switch (self) {
.north, .south => true,
.east, .west => false,
};
}
};
pub fn main() void {
const d: Direction = .north;
std.debug.print("{}\n", .{d.isVertical()});
}The output is:
trueAn enum value is not a string. It is a distinct value of its enum type.
const c: Color = .red;The value c is not the text "red". It is the enum value .red.
This matters when writing APIs. Use strings for external text. Use enums for internal choices.
const Mode = enum {
debug,
release_safe,
release_fast,
release_small,
};This is better than passing string names through the program. The compiler checks spelling. The compiler also checks that switches are complete.
Enums may also be inferred in function calls.
const Mode = enum {
read,
write,
};
fn open(mode: Mode) void {
_ = mode;
}
pub fn main() void {
open(.read);
}The argument .read works because the parameter type is Mode.
Use an enum when a value must be one of a known set of alternatives.
Examples:
const TokenKind = enum {
identifier,
number,
string,
plus,
minus,
eof,
};const FileState = enum {
closed,
open,
error_seen,
};const LogLevel = enum {
debug,
info,
warn,
err,
};An enum gives names to choices. It prevents invalid choices from being represented by accident.
Exercises.
7-13. Define an enum named Day with seven values.
7-14. Write a function isWeekend(day: Day) bool.
7-15. Define an enum named LogLevel and use a switch to convert it to a string.
7-16. Define an enum with tag type u8, assign explicit integer values, and print one value with @intFromEnum.