Last active
July 18, 2020 10:36
-
-
Save he-la/f23aa89611fd573cca380cdb76caf3ea to your computer and use it in GitHub Desktop.
Convert an enum to an error set in zig (ziglang.org)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const builtin = std.builtin; | |
// Convert an enum to an error set. | |
// | |
// This is quite a kludge that works by generating the ErrorSet using comptime | |
// (possible since a62e9bc8e5). Enum values are converted to error values using | |
// @enumToInt and @intoToError and banking on the hypothesis that error values | |
// are generated sequentially - this only works for enums with sequentially | |
// assigned values. | |
fn enum_to_errset(comptime the_enum: type) type { | |
const fields = switch (@typeInfo(the_enum)) { | |
.Enum => |e| e.fields, | |
else => unreachable, | |
}; | |
comptime var err_tinfos: [fields.len]builtin.TypeInfo.Error = undefined; | |
comptime var i = 0; | |
inline while (i < fields.len) : (i += 1) { | |
err_tinfos[i] = .{ | |
.name = fields[i].name, | |
.value = 0, // ignored | |
}; | |
} | |
const errset = @Type(.{ .ErrorSet = &err_tinfos }); | |
const err_start = switch (@typeInfo(errset)) { | |
.ErrorSet => |eset| (eset orelse unreachable)[0].value, | |
else => unreachable, | |
}; | |
// verify invariants for the make_error function to work | |
i = 0; | |
inline while (i < fields.len) : (i += 1) { | |
if (fields[i].value != i) | |
@compileError("enum_to_errset only works with sequential enum values"); | |
const tinfo = switch (@typeInfo(errset)) { | |
.ErrorSet => |eset| (eset orelse unreachable)[i], | |
else => unreachable, | |
}; | |
if (tinfo.value != err_start + i) | |
@compileError("Invariant violated: Zig compiler no longer assigns sequential error values. This breaks enum_to_errset."); | |
} | |
return struct { | |
const errset: type = errset; | |
const first_value: comptime_int = err_start; | |
// convert an enum value into the corresponding error | |
pub fn make_error(enum_val: the_enum) !void { | |
return @intToError(first_value + @enumToInt(enum_val)); | |
} | |
}; | |
} | |
test "enum to error set" { | |
const MyEnum = enum { | |
JustTesting, | |
TestingSomeMore, | |
AndEvenMore, | |
}; | |
const MyErrors = enum_to_errset(MyEnum); | |
MyErrors.make_error(.TestingSomeMore) catch |err| switch(err) { | |
error.TestingSomeMore => return, | |
else => unreachable, | |
}; | |
unreachable; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment