Thanks for your replies.
As an exercise for me, and also to learn more about Zig, I wanted to try writing a small wrapper for libpq. This isn’t intended as a to-be-used library, but more as an experiment for myself to see how certain things could be implemented when using existing C infrastructure.
First of all, I noticed that the Zig error types do not carry payload. As far as I understand, I need a separate mechnism to do this sort of specific error reporting. I’m not sure what’s the best approach here, but I attempted something like this:
pub const Db = struct {
const Self = @This();
pgConn: ?*c.PGconn,
pub fn connect(params: ConnectParams, msghdl: anytype) DbError!Db {
const conn = c.PQconnectdb(params.string) orelse return DbError.OutOfMemory;
if (c.PQstatus(conn) != c.CONNECTION_OK) {
defer c.PQfinish(conn);
if (@TypeOf(msghdl) != void) {
msghdl.errmsg(c.PQerrorMessage(conn));
}
return DbError.ConnectError;
}
return .{ .pgConn = conn };
}
};
If the caller doesn’t care about the error message, it’s possible to call Db.connect(…, {}). But when the error message is of interest, the caller may pass some handler that can print or copy the error message. I assumed that I could avoid any extra allocation for the error string if I do it this way, because the error string lives until I call PQfinish.
I think I don’t really need a nullable anytype as nullable is for runtime “nullability”. At compile-time, I guess the right way would be to pass a value of type void (i.e. {}) if I want to indicate that I don’t want diagnostics. But correct me please if this is a bad approach. Maybe there is also an entirely different way of (optionally) reporting the error string on connection failure.
Interesting, I wasn’t aware that null without any specific type information has its own type @TypeOf(null).
Rethinking about this, I don’t think it’s necessary to support .optional. But this makes me wonder, should I use {} (of type void) or null (of type @TypeOf(null)) to indicate that I don’t want diagnostics? Or my own special type like NoDiagnosticsPleaseDiscardAllErrorStrings?
To get back to my simple (dumbed down) example of an optional writer:
const std = @import("std");
const DoNotWrite = struct {};
const doNotWrite = .{};
pub fn foo(optwriter: anytype) !void {
switch (@TypeOf(optwriter)) {
void => {}, // is this better?
@TypeOf(null) => {}, // or this?
// or something like this:
DoNotWrite => {},
@TypeOf(doNotWrite) => {},
else => try optwriter.writeAll("Hello\n"),
}
}
pub fn main() !void {
try foo(std.io.getStdOut());
try foo({});
try foo(null);
try foo(DoNotWrite{});
try foo(doNotWrite);
}
Which of these variants seem best, when I only want “compile-time optionality”?
Thanks! I’m very happy to have found Zig and I’m eager to take a closer look at it and to understand all its concepts. Its simplicity reminds me a bit of Lua, but with a lot of stuff happening at compile-time and the efficiency of C.