3 ms·
Purely on the topic of `errdefer`, I believe the equivalent in Go would be: // must use named returns so you can observe the returned error func yourfu
by Groxx 5y ago
Purely on the topic of `errdefer`, I believe the equivalent in Go would be:
// 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.
- slimsag 5y agoActually, not only are you right about your errdefer Go implementation - but that's actually fairly common in Go code. Especially for tracing/logging of errors returned from functions; or for e.g. rolling back DB transactions in the event of an error. Here's some ~100 examples from my work's (Sourcegraph's) codebase[0] [0] https://sourcegraph.com/search?q=context:global+repo:%5Egithub%5C.com/sourcegraph/sourcegraph%24+defer+func%28%29+%7B%5Cn.*if+err+%21%3D+nil+count:all&patternType=regexp https://sourcegraph.com/search?q=context:global+repo:%5Egith...
- mjbrusso 5y agoThe equivalent in Visul Basic would be: On Error Goto line
- ziml77 5y agoI'd be interested to see if someone who works regularly with Go can show a cleaner way to do this. Because what I'm seeing here is just reinforcing my biases against Go. I've tried Go a couple times before but only ever read about Zig and yet I still find the Zig far easier to follow. It's much less verbose without being less explicit. (Also I'm not sure if the compiler would catch this, but there seems to be a bug in that Go code that's hard to see at a glance. You're never assigning a value to result)
- Groxx 5y agoI work regularly in Go - for an isolated example, that's about as clean as it gets. If you want to make it a little more middleware-y you could do this: func iferr(err *error, f func()) { if err != nil { f() } } // use func whatever(...) (err error) { thing := ... defer iferr(&err, thing.Method) } But that only works with an exact match, so that example can't match e.g. `Close() error`, so you still have to either make a ton of near-clones for different signatures, or use a ton of trivial closures (like with the manual version), to make the type system happy. Which can be worth it in a large enough project - `func()` and `func() error` will take care of like 90% of the signatures you'd defer anyway. I'd probably do that personally, unless I only needed it once or twice. There has been a somewhat similar check/handle proposal (close to "try"), but it has consistently fallen into "they're great for trivial cases, but they make it harder to have good error messages / wrap errors with additional context", which is absolutely necessary in Go since errors are so loosely typed and contain only explicitly-added information (no stacks by default, for example). So I'm glad they haven't happened yet.
- Groxx 5y agoAh, and: yes! You're right, it does fail to return any `result`. That was unintended, and as-written it won't complain, since `thing` is "used" (methods are called on it) and `result` does not need to be used: https://play.golang.org/p/iSU5YWyxODC https://play.golang.org/p/iSU5YWyxODC