Surprising behavior when consuming payload of tagged union passed to switch

Yeah, it’s a common idiom for sure, and if you use a temporary variable that is still potentially accessable after its deinitialization I would definitely use it.

I would suggest something like this:

const ConnectAttempt = union {
    ok: Connection,
    fail: ConnectFail,

    pub fn init(params: ConnectParams) ConnectAttempt { ... }

    pub fn deinit(attempt: *ConnectAttempt) void {
        switch (attempt) {
            .ok => |*conn| => conn.deinit(),
            .fail => |*fail| => fail.deinit(),
        }
    }
};

pub fn main() !void {
    const params = mylib.ConnectParams{ .string = "dbname=jbe" };
    var attempt = mylib.ConnectAttempt.init(params);
    defer attempt.deinit();
    switch (attempt) {
        .ok => |*conn| {
            // use conn here
        },
        .fail => |*fail| {
            std.debug.print("{s}\n", .{fail.message()});
        },
    }
}

This way initialization and deinitialization happen in the same scope right next to each other which helps avoid bugs and confusion IMO especially if you plan to add a lot of code to the switch prongs or more cases in the future.

2 Likes