I have a union that has a few fields that are void types, and I have the tag as an enum. For example:
const MyUnion = union(enum) {
a: Type1,
b: Type2,
c: Type3,
d: void,
e: void,
};
const tag = std.meta.Tag(MyUnion).e;
const x = [how???];
enum tags will coerce to their respective tagged union if their type is void, so you can just do this:
const x: MyUnion = tag;
2 Likes
A void type is created with an empty block: {}
An option would be to do:
const x = MyUnion{ .e = {} };
// Or using RLS
const x: MyUnion = .{ .e = {}};
// Or using Justus2308's option
const x: MyUnion = .e;
2 Likes
The tag isn’t comptime-known, e was just an example.
The compiler doesn’t like that the union also has non-void fields, and as such does not allow the coercion.
I know that the tag will only refer to a void though.
If you want to do this for the general case at runtime, you’ll need a switch statement or something. The compiler can’t tell that you’re only ever going to use fields with a void type so it won’t allow that syntax.
As @kristoff has pointed out inline prongs are the way to go.
You could also do something like this without @unionInit since you know that the field is going to be void:
fn coerce(tag: @typeInfo(MyUnion).@"union".tag_type.?) MyUnion {
switch (tag) {
inline .d, .e => |t| return t,
else => unreachable,
}
}
1 Like
That sounds great, but unionInit is confusing me.
Using
switch (tag) {
inline else => |ct_tag| {
return @unionInit(Token, @tagName(ct_tag), {});
},
}
Makes the compiler complain that {} should be a []u8, which makes no sense.
If you just inline else all values, @unionInit has to cover every single initialization case, so probably one of your union fields has []u8 and not void as a type. You’ll have to only inline the fields with void types (or do some further comptime magic to filter out the ones with void types if you feel like it’s necessary)
1 Like
Is one of the union variants a []u8? The inline switch will try to set any and all variants to void, which will not work if you have variants that are other types.
You probably want:
var empty = [0]u8{};
const x = switch (tag) {
// All void ones go here
.d, .e => @unionInit(Token, @tagName(tag), {}),
// For a struct or something
.a => @unionInit(Token, @tagName(tag), .{}),
// For a slice of u8,
.b => @unionInit(Token, @tagName(tag), &empty),
};
// or
const x = @unionInit(Token, @tagName(tag), switch (tag) {
.d, .e => {},
.a => .{},
.b => &empty,
});
Comptime magic achieved!
switch (tag) {
inline else => |ct_tag| {
if (@FieldType(Token, @tagName(ct_tag)) != void) unreachable;
return @unionInit(Token, @tagName(ct_tag), {});
},
}
4 Likes