top | item 43497199

(no title)

elvlysh | 11 months ago

You can do something like this:

   type errHandler struct {
      err error
   }

   func (eh *errHandler) getFirst() string {
      // stuff
      if err { eh.err = err }
      return result
   }

   func (eh *errHandler) doWith(input string) string {
      if eh.err != nil {
         return ""
      }
      //stuff
      if err { eh.err = err }
      return result
   }

   func (eh *errHandler) doFinally(input string) string {
      if eh.err != nil {
         return ""
      }
      //stuff
      if err { eh.err = err }
      return result
   }

   func (eh *errHandler) Err() error {
      return eh.err
   }

   func main() {
      eh := &errHandler{}
 
      first := eh.getFirst()
      second := eh.doWith(first)
      final := eh.doFinally(second)

      if err := eh.Err(); err != nil {
         panic(err)
      }
   }

discuss

order

9rx|11 months ago

You may as well use exception handlers if you're going to go there.

   func foo() (final int, err error) {
      defer func() {
         if e, ok := recover().(failure); ok {
            err = e
         } else {
            panic(e)
         }
      }()
      first := getFirst()
      doWith(first)
      final = doFinally()
      return
   }
encoding/json does it. It's okay if you understand the tradeoffs.

But look at what you could have wrote:

   func foo() (int, error) {
      first, err := getFirst()
      if err != nil {
         return 0, ErrFirst
      }

      err = doWith(first)
      if err != nil {
         return 0, ErrDo
      }


      final, err := doFinally()
      if err != nil {
         return 0, ErrFinally
      }

      return final, nil
   }
This one is actually quite nice to read, unlike the others, and provides a better experience for the caller too – which is arguably more important than all other attributes.

Dylan16807|11 months ago

And with some error utilities you could do this:

  func foo() (int, error) {
      first := getFirst()?
      doWith(first)?
      return doFinally()
  }
or this:

  func foo() (int, error) {
      first := getFirst() % ErrFirst
      doWith(first) % ErrDo
      return doFinally() % ErrFinally
  }
The first one is a significant upgrade over the exception version. It cuts out half the code and makes the early return points explicit.

I think something similar to the second one is also nice to read, and it gives the same improved experience to the caller as your suggestion.

elvlysh|11 months ago

> You may as well use exception handlers if you're going to go there.

I didn't know about this trick, thanks for sharing.

tubthumper8|11 months ago

> You can do something like this

Do people actually do this? Is it included in the standard library? If not, should it be?