Skip to content

Instantly share code, notes, and snippets.

@he-la
Last active July 18, 2020 10:36
Show Gist options
  • Save he-la/f23aa89611fd573cca380cdb76caf3ea to your computer and use it in GitHub Desktop.
Save he-la/f23aa89611fd573cca380cdb76caf3ea to your computer and use it in GitHub Desktop.
Convert an enum to an error set in zig (ziglang.org)
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