// must use named returns so you can observe the returned error
func yourfunc() (result thingtype, err error) {
thing := constructIt()
defer func(){
if err != nil {
thing.Close()
}
}()
// use thing to do a few things that could error
err = thing.setup()
if err != nil {
return nil, err
}
other, err := thing.other()
if err != nil {
return // naked returns are fine too
}
// etc
return
}
Which is...... technically possible, and not even all that bad (as much as I strongly dislike named returns, since I find them very error-prone), but the fact that I have not ever seen this done does sorta reveal how much friction there is. Instead, I usually see manual additions of err-cleanup to relevant branches, and I've seen that pattern fail repeatedly as well (especially when the code is modified much later by some other person).The Zig equivalent seems like it would be roughly (I don't know Zig)
func yourfunc() thingtype {
thing := constructIt()
errdefer thing.Close()
// use thing to do a few things that could error
try thing.setup()
other = try thing.other()
return thing
}
which is quite a bit more palatable and future-mistake-resistant. A sizable chunk of that is due to `try` of course, but still.