301 lines
6.4 KiB
Markdown
301 lines
6.4 KiB
Markdown
# 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.
|
|
|
|
```zig
|
|
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
|
|
|
|
```zig
|
|
// 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
|
|
|
|
```zig
|
|
var it = colors.iterator();
|
|
while (it.next()) |color| {
|
|
std.debug.print("{}\n", .{color});
|
|
}
|
|
```
|
|
|
|
## EnumMap
|
|
|
|
Map from enum to value. Fixed-size, zero allocation.
|
|
|
|
```zig
|
|
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
|
|
|
|
```zig
|
|
// 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
|
|
|
|
```zig
|
|
// 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.
|
|
|
|
```zig
|
|
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
|
|
|
|
```zig
|
|
// 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.
|
|
|
|
```zig
|
|
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
|
|
|
|
```zig
|
|
// 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:
|
|
|
|
```zig
|
|
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
|
|
|
|
```zig
|
|
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
|
|
|
|
```zig
|
|
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 tracking
|
|
- `EnumMap`: Sparse, only stores present values
|
|
- `EnumArray`: Dense, all values always present
|
|
- All are fixed-size, zero-allocation, copyable by value
|
|
- Use `std.StaticBitSet` for non-enum integer sets
|
|
- Works with non-exhaustive enums (explicit fields only)
|