DEV Community

Cover image for A Beginner's Guide to Enums and Error Sets in Zig
HexShift
HexShift

Posted on

A Beginner's Guide to Enums and Error Sets in Zig

Enums and error sets in Zig offer powerful ways to model states and failures with clarity and precision. In this tutorial, we’ll cover the basics, then walk through real-world uses of both types — and how they play nicely together.


Step 1: Defining Enums

Enums let you define a type with named values:

const Direction = enum {
    North,
    South,
    East,
    West,
};
Enter fullscreen mode Exit fullscreen mode

You use them like this:

const dir = Direction.North;
Enter fullscreen mode Exit fullscreen mode

Zig will guarantee at compile time that dir is one of the defined directions.


Step 2: Switching on Enums

Zig encourages exhaustive switching — the compiler checks for missing cases:

switch (dir) {
    Direction.North => std.debug.print("Going up\n", .{}),
    Direction.South => std.debug.print("Going down\n", .{}),
    Direction.East => std.debug.print("Going right\n", .{}),
    Direction.West => std.debug.print("Going left\n", .{}),
}
Enter fullscreen mode Exit fullscreen mode

If you forget a case, Zig will complain — and that’s a good thing.


Step 3: Using Tagged Unions with Enums

You can pair an enum with associated data:

const Shape = union(enum) {
    Circle: f32,
    Square: f32,
};
Enter fullscreen mode Exit fullscreen mode

Now Shape can either be a Circle with a radius, or a Square with a side length:

const s = Shape{ .Circle = 5.0 };
Enter fullscreen mode Exit fullscreen mode

You can match and extract values:

switch (s) {
    Shape.Circle => |r| std.debug.print("Circle radius: {}\n", .{r}),
    Shape.Square => |s| std.debug.print("Square side: {}\n", .{s}),
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Error Sets

Error sets are custom error types. They're first-class citizens in Zig:

const MyError = error{
    NotFound,
    PermissionDenied,
};
Enter fullscreen mode Exit fullscreen mode

You can use them as return types:

fn read() MyError!void {
    return MyError.NotFound;
}
Enter fullscreen mode Exit fullscreen mode

And handle them with catch, try, or a switch:

const result = read() catch |err| switch (err) {
    MyError.NotFound => std.debug.print("Missing file\n", .{}),
    MyError.PermissionDenied => std.debug.print("Access denied\n", .{}),
};
Enter fullscreen mode Exit fullscreen mode

✅ Pros and ❌ Cons of Enums & Error Sets

✅ Pros:

  • 🚦 Clear, type-safe control flow
  • 🧠 Enforced exhaustiveness in switches
  • 🔍 Custom error modeling with compiler support
  • 🧱 Combine enums + unions for expressive state

❌ Cons:

  • ⚠️ No implicit numeric values for enums (unlike C)
  • 🔄 Switching can be verbose if not using else or _
  • 🧵 Nested switches may become hard to read without care

Summary

Zig’s enums and error sets give you precise, predictable ways to model data and errors. Instead of relying on exceptions or raw integers, you build robust control flow with the compiler as your co-pilot. Learning how to use these together will level up both the clarity and safety of your codebase.


Unlock the full potential of ZIP file error handling with my comprehensive 17-page PDF guide! For just $10, you’ll gain access to expert tips, best practices, and real-world solutions specifically focused on error handling, security, and optimization in ZIP processing. Don’t miss out – click here to get your copy now and start mastering ZIP file error handling like a pro!

If this was helpful, you can also support me here: Buy Me a Coffee

Top comments (0)