3 ms·
I've mostly evolved to making err a named return parameter, and inverting the err != nil check. For example: func foo() (err error) { var x any if x
by dpifke 3y ago
I've mostly evolved to making err a named return parameter, and inverting the err != nil check. For example:
func foo() (err error) {
var x any
if x, err = bar(); err == nil {
err = baz(x)
}
if err == nil {
err = bat()
}
if err != nil {
err = fmt.Errorf("%w doing foo <additional info here>", err)
}
return
}
This feels somewhat cleaner to me, in particular by combining error handling (in this case just a simple wrap) in a single place at the end of the function.
- marcus_holmes 3y agoCurious why you're using fmt.Fprintf and not fmt.Errorf? Or is that a typo? And I think you're going to have problems with this pattern if you join a team using Go in an organisation. The `if err != nil` pattern is the norm, and everyone's used to it (and the regular cadence of Go code; "do the thing, check the error, do the thing, check the error" is very readable).
- dpifke 3y agoThat was a typo, thanks for pointing it out. :)
- preseinger 3y agoplease don't do this it obfuscates the control flow, specifically the value that is actually returned early returns on errors are good, not bad edit you want func foo() error { x, err := bar() if err != nil { return fmt.Errorf("bar: %w", err) } if err := baz(x); err != nil { return fmt.Errorf("baz: %w", err) } if err := bat(); err != nil { return fmt.Errorf("bat: %w", err) } return nil }
- meling 3y agoYes, I absolutely agree with this. I think there is great value in returning early on error; think of them as guards checking that you have the values you need for the next logic step. In the original version you may have to read the whole function to understand why it failed.
- erik_seaberg 3y agoI’m all for generating that. I don’t want it in source where rereading it wastes expensive developers’ time and mistakes become possible.
- meling 3y agoCopilot is pretty good at recognizing and generating the error check ; it will even propose error messages for you. Clearly you may want to change it to your specific case. So I don’t think dev time will be significantly slower. My experience has been positive. I think the go plugin also has some helpers for this pattern, but I don’t recall exactly how they work.
- deleted 3y ago[deleted]
- preseinger 3y agonope error handling (as expressed here) is equivalent in priority to core business logic it absolutely belongs in source, because it is important for developers to see
- deleted 3y ago[deleted]
- dpifke 3y agoSome other variants I've played with: func foo() (err error) { var x any if x, err = bar(); err != nil { goto fooError } if err = baz(x); err != nil { goto fooError } if err = bat(); err != nil { goto fooError } return fooError: return fmt.Errorf("%w doing foo <additional info here>", err) } Or: func foo() (err error) { defer func() { if err != nil { err = fmt.Errorf("%w doing foo <additional info here>", err) } }() var x any if x, err = bar(); err != nil { return } if err = baz(x); err != nil { return } err = bat() return } Seeking out different patterns is obviously most applicable in cases where error handling is actually doing something useful or more complicated than just wrapping the error. (My comment was meant to spur first principles discussion from intellectually curious folks, not "nobody does it that way" or "don't do that" edicts. Much of the argument against adding additional language features for error handling is that many of them aren't any better than what can be accomplished already, using existing syntax but different code style conventions. The goto pattern in particular is found all over the stdlib.)
- euroderf 3y agoI like your second variant - some DRY, moved to the top of the function. This would reduce fiddling during preliminary development, with loggers and Printf's and whatevers, until an error architecture stabilizes.
- preseinger 3y ago> The goto pattern in particular is found all over the stdlib. in generated code, sure -- that's why it exists, to support codegen it's sometimes abused to manage for loop control flow but the stdlib is definitely not some platonic ideal -- it's a decade+ old code base which has suffered all of the indignities of organic growth it's full of bad code and terrible anti-patterns (good stuff, too!)