I don’t understand what you mean here. Why are you avoiding the active_tag
and for which parts specifically? (your first if actually uses it)
I don’t know what you are trying to do with all the conversion to *anyopaque
and back to value. I think you could do this instead:
const std = @import("std");
fn retrieveFieldFromUnion(
comptime UnionType: type,
comptime desired_tag: std.meta.Tag(UnionType),
union_value: UnionType, // may be a `*const` ?
) !@FieldType(
UnionType,
@tagName(desired_tag),
) {
if (std.meta.activeTag(union_value) != desired_tag)
return error.cantAccesInactiveTag; // TODO: may just return a null
return switch (union_value) {
inline else => |x| x,
};
}
const Example = union(enum) {
foo: u32,
bar: u8,
};
pub fn main() !void {
const f: Example = .{ .foo = 132323 };
std.debug.print("value: {}\n", .{try retrieveFieldFromUnion(Example, .foo, f)});
}
But instead I would use one of those:
const std = @import("std");
const Example = union(enum) {
foo: u32,
bar: []const u8,
pub fn unwrap(self: *const Example, comptime tag: std.meta.Tag(Example)) !std.meta.TagPayload(Example, tag) {
if (std.meta.activeTag(self.*) != tag) return error.InvalidUnionAccess;
return @field(self, @tagName(tag));
}
};
pub fn GetUnion(T: type) type {
return switch (@typeInfo(T)) {
.pointer => |p| p.child,
else => T,
};
}
pub fn Tag(T: type) type {
return std.meta.Tag(GetUnion(T));
}
pub fn activeTag(val: anytype) Tag(@TypeOf(val)) {
return std.meta.activeTag(switch (@typeInfo(@TypeOf(val))) {
.pointer => val.*,
else => val,
});
}
pub fn access(val: anytype, comptime tag: Tag(@TypeOf(val))) !std.meta.TagPayload(GetUnion(@TypeOf(val)), tag) {
if (activeTag(val) != tag) return error.InvalidUnionAccess;
return @field(val, @tagName(tag));
}
pub fn main() !void {
const e: Example = .{ .bar = "hello" };
std.debug.print("value: {s}\n", .{try e.unwrap(.bar)});
std.debug.print("value: {s}\n", .{try access(&e, .bar)});
std.debug.print("value: {s}\n", .{try access(e, .bar)});
}
However because tagged unions are already safety checked in safe modes, it isn’t really required to manually check for the active tag, so I would actually do this instead:
const std = @import("std");
const Example = union(enum) {
foo: u32,
bar: []const u8,
pub fn unwrap(self: *const Example, comptime tag: std.meta.Tag(Example)) std.meta.TagPayload(Example, tag) {
return @field(self, @tagName(tag));
}
};
pub fn GetUnion(T: type) type {
return switch (@typeInfo(T)) {
.pointer => |p| p.child,
else => T,
};
}
pub fn Tag(T: type) type {
return std.meta.Tag(GetUnion(T));
}
pub fn access(val: anytype, comptime tag: Tag(@TypeOf(val))) std.meta.TagPayload(GetUnion(@TypeOf(val)), tag) {
return @field(val, @tagName(tag));
}
pub fn main() !void {
const e: Example = .{ .bar = "hello" };
const f: Example = .{ .foo = 45 };
std.debug.print("value: {s}\n", .{e.unwrap(.bar)});
std.debug.print("value: {s}\n", .{access(&e, .bar)});
std.debug.print("value: {s}\n", .{access(e, .bar)});
const lst = [_]Example{ e, f };
for (lst) |v| {
switch (v) {
.bar => |val| std.debug.print("value: {s}\n", .{val}),
inline else => |val| std.debug.print("value: {}\n", .{val}),
}
}
}
Then through testing and fuzzing you can make sure that your code handles all possible union values correctly.