I like what we get from this, but the semantics seem not-right to me.
I wrote an issue (closed under ‘no language proposals’ policy, which, fair) about changing the semantics of errdefer |err|
to actually capture the error. Meaning that an errdefer
block cannot return, but an errdefer |err|
block must return.
If it worked that way, then defer
could have an analogue:
defer |ret| {
if (ret) |ok| {
parent.closeContainer(&iter);
return ok;
} else |err| {
parent.abandonContainer(&iter);
return err;
}
}
This wouldn’t come for free: if you think about the control flow implied by several nested defer
and errdefer
blocks like this, it could get very gnarly and hard to follow pretty quick.
But I don’t like “capturing defer gets an error but only if one was thrown”, it seems too arbitrary. The relationship between errdefer
and errors is naturally mirrored by the same relationship between defer
and returned values, giving defer
an ?ErrorSet
(which is a weird type) seems synthetic.
Given that we can do this:
That semantics amounts to syntax sugar for this one. Not quite, it would in fact be easier to use, but, close.
My main argument for changing errdefer |err|
is that every other use of |cap|
in Zig actually captures what it shows. In errdefer |err|
it just borrows it to get a chance to look at it.
“Catch and release” defers are also a bit weird in that it would break the invariant that only one return
statement executes per a call. But I don’t think it does so in a bad way actually, because return
already means “run all the deferred blocks and then return” so the “re-throw” we’re talking about here isn’t all that exotic.
It might make sense to limit a block to one capturing defer, error or otherwise, or even one per function, on the premise that more than this leads to code which no mere human can understand. Maybe even require it to be the first one defined, therefore the last to run.
I’m not generally a fan of arguments which boil down to “someone can use this to write bad code”, which shouldn’t be confused with the superficially similar “this feature is hard to use correctly and that will result in bad code too often to justify what we’d get from it”.
But it’s unclear to me where any variation of this idea lies along that axis.