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).