Surprising behavior when consuming payload of tagged union passed to switch

I read, for exampe in this article, that it is a common idiom to invalidate the memory of a structure at the end of a deinit function by setting self.* = undefined. For that, I need self to be a pointer to a non-const struct. In my case it is something like this:

pub const ConnectFail = struct {
    code: DbConnError,
    pg_conn: ?*c.PGconn,
    pub fn message(self: ConnectFail) [*:0]const u8 {
        return c.PQerrorMessage(self.pg_conn orelse return "libpq could not allocate memory");
    }
    pub fn deinit(self: *ConnectFail) void {
        if (self.pg_conn) |conn| {
            c.PQfinish(conn);
        }
        self.* = undefined;   
    }
};

(Having experimentally modified my approach here to refrain from demanding a message handler but returning a “to-be-deinit(ed)” error struct on failure.)

I then have to write something like that when using this code:

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

So what I attempted was basically to have a switch statement that differentiates between the two outcomes of the connection attempt (successful connect and non-succeessful connection with a to-be-free’d error status).