4 ms·
If there a paradigm for handling multiple potential errors in a function? I'm usually stuck using: reply, err := redis.Givemethevalue(key) if err != nil
by jonesb6 11y ago
If there a paradigm for handling multiple potential errors in a function?
I'm usually stuck using:
reply, err := redis.Givemethevalue(key)
if err != nil {
return err
}
thing, err2 := postgres.getById(reply)
if err2 != nil {
return err2
}
- lobster_johnson 11y agoYes: reply, err := redis.Givemethevalue(key) if err != nil { return err } thing, err := postgres.getById(reply) if err != nil { return err } Go allows you to assign with := if at least one of the left-hand-side variables is new.
- forgotAgain 11y agoI use var err error at the beginning of the function if needed.
- jgome 11y agoThis helps prevent variable shadowing when using :=, which is yet another weird feature of the language... BTW, if you really want to make sure you don't make mistakes, use the compiler tools, or, better yet, https://github.com/alecthomas/gometalinter https://github.com/alecthomas/gometalinter .
- brandonbloom 11y agoI tend to use structs like mini "dynamically" scoped namespaces for breaking up hairy tasks. Pattern looks like: struct fooer { x int blah string // ... more state err error } func Foo(x int) (string, error) { f := &fooer{x: x}; defer f.cleanup() f.stepOne() f.stepTwo() f.stepThree() return f.blah, f.err } func (f *fooer) stepOne() { // ... do stuff and maybe set f.err } func (f *fooer) stepTwo() { if f.err != nil { return } // ... do stuff and maybe set f.err } func (f *fooer) stepThree() { if f.err != nil { return } // ... do stuff and maybe set f.err; set f.blah } func (f *fooer) cleanup() { // ... } While this is initially a tad more verbose, it has some nice benefits. For one thing, you can easily fmt.Printf("%#v", f) to dump all the relevant state. However, it also tends to be more resilient to refactoring: - No method signatures to patch up. - Easier to non-local jump via return if you have some unusual control flow, etc. - You can easily find all references to `f.err` to verify your error handling. - The method names and step list take on a self-documenting quality. Another key insight I had when learning to tolerate Go: It's ok to do throw away work. You don't need to abort instantly on failure, you can just make sure code paths handle zero values or other invalid states more robustly. For example, I sometimes add a function like: func (f *fooer) fail(err error) { if f.err == nil { f.err = err } } And then just add sprinkle some f.fail() calls, make code handle nils, invalid states, etc, and make cleanup code idempotent. Seems to work out much more nicely than if err, return.