6.4 KiB
6.4 KiB
std.enums
Utilities for working with enums: sets, maps, arrays, and iteration backed by bit operations.
EnumSet
Bit-backed set of enum values. Zero allocation, copyable by value.
const std = @import("std");
const Color = enum { red, green, blue, yellow };
const ColorSet = std.enums.EnumSet(Color);
// Initialize
var colors = ColorSet.initEmpty();
var all = ColorSet.initFull();
// Struct-style init
var primary = ColorSet.init(.{
.red = true,
.green = true,
.blue = true,
.yellow = false,
});
// From slice
var some = ColorSet.initMany(&.{ .red, .blue });
// Single element
var just_red = ColorSet.initOne(.red);
EnumSet Operations
// Insert/remove
colors.insert(.red);
colors.remove(.blue);
colors.toggle(.green);
colors.setPresent(.yellow, true);
// Check
if (colors.contains(.red)) {
// red is in set
}
const n = colors.count(); // number of elements
// Set operations (in-place)
colors.setUnion(other); // add all from other
colors.setIntersection(other); // keep only common
colors.toggleSet(other); // XOR
colors.toggleAll(); // invert all
// Set operations (return new set)
const u = colors.unionWith(other);
const i = colors.intersectWith(other);
const x = colors.xorWith(other);
const d = colors.differenceWith(other); // colors - other
const c = colors.complement(); // all except colors
// Comparison
if (colors.eql(other)) { }
if (colors.subsetOf(other)) { }
if (colors.supersetOf(other)) { }
EnumSet Iteration
var it = colors.iterator();
while (it.next()) |color| {
std.debug.print("{}\n", .{color});
}
EnumMap
Map from enum to value. Fixed-size, zero allocation.
const Color = enum { red, green, blue };
const ColorMap = std.enums.EnumMap(Color, u32);
// Empty map
var map = ColorMap{};
// Struct-style init (null = not present)
var scores = ColorMap.init(.{
.red = 100,
.green = 50,
.blue = null, // not in map
});
EnumMap Operations
// Insert
map.put(.red, 42);
// Get
if (map.get(.red)) |value| {
std.debug.print("red = {}\n", .{value});
}
// Get with default
const value = map.getOrDefault(.blue, 0);
// Get pointer
if (map.getPtr(.red)) |ptr| {
ptr.* += 1; // modify in place
}
// Remove
map.remove(.red);
// Check
if (map.contains(.red)) { }
// Count
const n = map.count();
EnumMap Iteration
// Iterate entries
var it = map.iterator();
while (it.next()) |entry| {
std.debug.print("{}: {}\n", .{ entry.key, entry.value.* });
}
// Iterate keys only
var key_it = map.keyIterator();
while (key_it.next()) |key| {
std.debug.print("{}\n", .{key});
}
EnumArray
Dense array indexed by enum. All values always present.
const Color = enum { red, green, blue };
const ColorArray = std.enums.EnumArray(Color, u32);
// Initialize all to same value
var arr = ColorArray.initFill(0);
// Struct-style init (all must be present)
var rgb = ColorArray.init(.{
.red = 255,
.green = 128,
.blue = 64,
});
// Access
const r = rgb.get(.red); // 255
rgb.set(.green, 200);
rgb.getPtr(.blue).* = 100;
EnumArray Iteration
// By key
for (std.enums.values(Color)) |color| {
std.debug.print("{}: {}\n", .{ color, rgb.get(color) });
}
// Direct slice access
const slice = rgb.values; // [3]u32
EnumIndexer
Convert between enum values and dense indices.
const Indexer = std.enums.EnumIndexer(Color);
const idx = Indexer.indexOf(.green); // 1
const color = Indexer.keyForIndex(1); // .green
const count = Indexer.count; // 3
Utility Functions
// Get all values as slice
const colors = std.enums.values(Color); // [3]Color
// Safe tag name (works with non-exhaustive)
const name = std.enums.tagName(Color, .red); // "red" or null
// Safe int-to-enum
const maybe = std.enums.fromInt(Color, 1); // ?.green
Direct Enum Array (Sparse Enums)
For enums with gaps in values:
const Sparse = enum(u8) { a = 1, b = 5, c = 10 };
// Create array indexed by enum int value
const arr = std.enums.directEnumArray(
Sparse,
bool,
8, // max_unused_slots (gaps allowed)
.{ .a = true, .b = false, .c = true },
);
// arr is [11]bool, indexed by @intFromEnum
Complete Example: Permission System
const std = @import("std");
const Permission = enum {
read,
write,
execute,
admin,
};
const Permissions = std.enums.EnumSet(Permission);
const User = struct {
name: []const u8,
perms: Permissions,
};
fn canAccess(user: User, required: Permissions) bool {
// User must have all required permissions
return required.subsetOf(user.perms);
}
pub fn main() void {
const admin = User{
.name = "admin",
.perms = Permissions.initFull(),
};
const reader = User{
.name = "reader",
.perms = Permissions.initOne(.read),
};
const write_required = Permissions.initMany(&.{ .read, .write });
std.debug.print("admin can write: {}\n", .{canAccess(admin, write_required)}); // true
std.debug.print("reader can write: {}\n", .{canAccess(reader, write_required)}); // false
}
Complete Example: State Machine Transitions
const std = @import("std");
const State = enum { idle, running, paused, stopped };
const Event = enum { start, pause, resume, stop };
const TransitionMap = std.enums.EnumMap(Event, State);
const StateTransitions = std.enums.EnumArray(State, TransitionMap);
const transitions = StateTransitions.init(.{
.idle = TransitionMap.init(.{ .start = .running, .stop = .stopped }),
.running = TransitionMap.init(.{ .pause = .paused, .stop = .stopped }),
.paused = TransitionMap.init(.{ .resume = .running, .stop = .stopped }),
.stopped = TransitionMap{}, // no transitions from stopped
});
fn nextState(current: State, event: Event) ?State {
return transitions.get(current).get(event);
}
pub fn main() void {
var state = State.idle;
state = nextState(state, .start) orelse state; // -> running
state = nextState(state, .pause) orelse state; // -> paused
state = nextState(state, .resume) orelse state; // -> running
std.debug.print("Final state: {}\n", .{state});
}
Notes
EnumSet: Bit-backed, use for presence trackingEnumMap: Sparse, only stores present valuesEnumArray: Dense, all values always present- All are fixed-size, zero-allocation, copyable by value
- Use
std.StaticBitSetfor non-enum integer sets - Works with non-exhaustive enums (explicit fields only)