6 ms·
It wasn't clear from the examples, and the gist doesn't have a `deinit` method, so what happens if an error needs to own data? > Here, sqlite.ErrorPayload.init
by latch 7mo ago
It wasn't clear from the examples, and the gist doesn't have a `deinit` method, so what happens if an error needs to own data?
> Here, sqlite.ErrorPayload.init saves 500 bytes of error message from sqlite
Who owns those 500 bytes and where are they being freed?
- srcreigh 7mo agoIt's just stored as a [256]u8 in the struct. // sqlite.zig pub const ErrorPayload = struct { message: [256]u8, pub fn init(db: *c.sqlite3) @This() { var self = std.mem.zeroes(@This()); var fw = std.Io.Writer.fixed(self.message[0..]); _ = fw.writeAll(std.mem.span(c.sqlite3_errmsg(db))) catch |err| switch (err) { error.WriteFailed => return self, // full }; return self; } };
- latch 7mo agoAnd what pattern would you recommend if you needed to allocate?
- dns_snek 7mo agoThe diagnostic struct could contain a caller-provided allocator field which the callee can use, and a deinit() function on the diagnostic struct which frees everything.
- srcreigh 7mo agoIt's worth every effort to avoid situations where a function creates extra clean up responsibilities to the caller only on error conditions. If I really needed a large error payload, too big to fit on the stack, I'd probably want to do something like this: const errbuf = try alloc.alloc(u8, 1024*1024*1024); module.setErrorBuf(&errbuf) defer { module.setErrorBuf(null); alloc.free(errbuf); } var func_diag = diagnostics.OfFunction(module.func){}; module.func(foo, bar, &func_diag) catch |err| switch (err) { error.BigPayload => { const payload = func_diag.get(error.BigPayload); // The payload can reference the 1MiB of data safely here, // and it's cleaned up automatically. } }