What would be the intended semantics of a function, which uses RLS, but also can fail in the middle? Ie, something like this:
fn f() !S {
return .{
.x = try g(),
.y = try h(),
};
}
Would x
and y
of the result be updated in-place (risking partially initialized state, if h
, but not g
, returns an error), or would this force copies? Right now, there are copies
const std = @import("std");
const S = struct {
x: i32,
y: i32,
};
fn f() !S {
return .{
.x = try g(),
.y = try h(),
};
}
fn g() !i32 {
return 92;
}
fn h() !i32 {
return error.oups;
}
pub fn main() void {
var s: S = .{ .x = 0, .y = 0 };
s = f() catch {
std.debug.print("{}\n", .{s}); // Prints 0, 0 in Debug and ReleaseFast
return;
};
}
but I am wondering how intentional is that.
Context: at TigerBeetle, we have a central struct, Replica
, which consists of various directly embedded substructs, and which has humongous size-of. Each of subparts (and subparts of subparts) has an init function, and many inits can fail.
The semantics we want here is to everything to be constructed in-place, and for de-inits of already initialized parts to run if case of an error in the middle.
It looks like the best way to implement this is probably just passing an out parameter, doing an in-place construction manually, but I am wondering if we could actually use RLS here?