How to get tagged union payload from anytype parameter

How do I obtain the union payload from a union provided as an anytype parameter?

fn foo (my_union: anytype) void {
    const payload = ???;
    std.debug.print("{}", .{payload});
}

test "union payload" {
    const MyUnion = union(enum) {
        num: u8,
        num2: u16,
    };

    // this should print 3
    foo(MyUnion{.num= 3});
}

switch for the type first and then for the union.

const std = @import("std");

const MyUnion = union(enum) {
    num: u8,
    num2: u16,
};

fn foo(my_union: anytype) void {
    const payload = switch (@TypeOf(my_union)) {
        MyUnion => switch (@as(MyUnion, my_union)) {
            .num => |num| num,
            .num2 => |num2| num2,
        },
        else => unreachable,
    };
    std.debug.print("{}", .{payload});
}

test "union payload" {

    // this should print 3
    foo(MyUnion{ .num = 3 });
}

In my use case, foo cannot know the types it may be called with. foo is part of the library and users call foo with their own unions.

You can switch with inline else to get the payload. You might not need it but i’ve also shown how you can capture tag.

const std = @import("std");

const MyUnion = union(enum) {
    num: u8,
    num2: u16,
};

fn foo(my_union: anytype) void {
    switch (my_union) {
        // tag is comptime known and may be used in @field(my_union, @tagName(tag))
        // or @unionInit(MyUnion, @tagName(tag), payload)
        inline else => |payload, tag| {
            std.debug.print("{s} {}\n", .{ @tagName(tag), payload });
        },
    }
}

test "union payload" {
    foo(MyUnion{ .num = 3 }); //  prints 'num 3'
    foo(MyUnion{ .num2 = 4 }); // prints 'num2 4'

}

More info https://ziglang.org/documentation/0.13.0/#Inline-Switch-Prongs

5 Likes

That’s great!

If restructured to the requested form, it becomes:

const std = @import("std");

fn foo(my_union: anytype) void {
    const payload = switch (my_union) {
        inline else => |payload| payload,
    };
    std.debug.print("{}", .{payload});
}

test "union payload" {
    const MyUnion = union(enum) {
        num: u8,
        num2: u16,
    };

    // this should print 3
    foo(MyUnion{ .num = 3 });
}
2 Likes

You’ll have problems with this if you add more types to the union which don’t coerce such as a []const u8 field. Thats why i moved the print within switch prong.

2 Likes