It just occured to me with switch on non exhaustive enums landing in 0.15.1 it is now trivial to check if value is valid enum variant or not.
const Piece = enum(u8) {
king = 0,
queen,
knight,
bishop,
rock,
pawn,
_,
pub fn isValid(p: Piece) bool {
return switch (p) {
else => true,
_ => false,
};
}
}
So for example std.enums.fromInt can be reimplemented without for loop
const std = @import("std");
const Piece = enum(u8) {
king = 0,
queen,
knight,
bishop,
rock,
pawn,
};
pub fn oldFromInt(comptime E: type, integer: anytype) ?E {
const enum_info = @typeInfo(E).@"enum";
if (!enum_info.is_exhaustive) {
if (std.math.cast(enum_info.tag_type, integer)) |tag| {
return @enumFromInt(tag);
}
return null;
}
// We don't directly iterate over the fields of E, as that
// would require an inline loop. Instead, we create an array of
// values that is comptime-know, but can be iterated at runtime
// without requiring an inline loop.
// This generates better machine code.
for (std.enums.values(E)) |value| {
if (@intFromEnum(value) == integer) return @enumFromInt(integer);
}
return null;
}
pub fn NonExhaustive(comptime E: type) type {
var info = @typeInfo(E);
if (!info.@"enum".is_exhaustive) return E;
info.@"enum".is_exhaustive = false;
info.@"enum".decls = &.{};
return @Type(info);
}
pub fn newFromInt(comptime E: type, integer: anytype) ?E {
const enum_info = @typeInfo(E).@"enum";
const trunked = std.math.cast(enum_info.tag_type, integer) orelse return null;
const value: NonExhaustive(E) = @enumFromInt(trunked);
switch (value) {
else => return @enumFromInt(@intFromEnum(value)),
_ => return if (!enum_info.is_exhaustive) value else null,
}
return null;
}
test oldFromInt {
try std.testing.expectEqual(Piece.king, oldFromInt(Piece, @as(usize, 0)).?);
try std.testing.expectEqual(null, oldFromInt(Piece, @as(u8, 7)));
}
test newFromInt {
try std.testing.expectEqual(Piece.king, newFromInt(Piece, @as(usize, 0)).?);
try std.testing.expectEqual(null, newFromInt(Piece, @as(u8, 7)));
// even works with comptime_int. This test case will give compile error for `fromInt`
try std.testing.expectEqual(Piece.king, newFromInt(Piece, 0).?);
try std.testing.expectEqual(null, newFromInt(Piece, 7));
}
I checked godbolt and it seems to generate roughly similar assembly Compiler Explorer.
I would assume it would faster for comptime evaluation tho (at least its O(1) branch quota instead of O(n))
Maybe relevant to previous discussion How to check if deserialized exhaustive enum value is legal?