Live data from Hacker News

Exploring Error Handling Patterns in Go

8thlight.com

31–40 of 80 posts

Re: Exploring Error Handling Patterns in Go

#31
post #4

if err != nil return err if err != nil return err if err != nil return err https://github.com/docker/cli/search?q=%22if+err+%21%3D+nil%... https://github.com/kubernetes/kubernetes/search?q=%22if+err+... https://github.com/coreos/etcd/search?q=%22return+err%22&uns... https://github.com/influxdata/influxdb/search?q=%22if+err+%2... The reality of Go's error handling is that you just implement exactly what exception bubb…

Error/Either monads are the perfect middle ground IMHO. You get errors as data types and an efficient way to abstract away the boilerplate associated with it.

Yes, I quite liked this about Rust's `Result` and `Option` type and using monads in general but I don't think Golang could achieve this pragmatically without generics.

Re: Exploring Error Handling Patterns in Go

#32
post #10
post #7

Earlier quoted context omitted.

This is not correct. Exceptions do different things than report an error. They unwind the stack. That's why they are called exceptions and not errors. One important benefit of Go's error handling pattern is readability. With exceptions, it's not easy to see who handles it and where. There is indeed less code, and that's nice for the writer, but from the reader perspective, error handling becomes obscure. And from the…

At the cost of making the entire logic's readability less which to me is more important than sometimes getting confused where errors bubble up to. The philosophy is different when, for example the author of Ruby wanted to make coding fun for programmers and does a good job at it and Go is sticking to 'this must be right' approach and breaks some people's heart. Personally I'd appreciate being more 'fun'.

Depends on your definition of fun, I guess. I personally don't like Ruby because of that fun-factor. In most cases, it makes programming easier for the novice, but more complicated for the experienced.

This is because to make it easier for the novice, there are all kinds of constructs that try to make the code imitate normal English. But coding software is a completely different thing than writing text, thus the English-like front is in fact a smoke screen that hides the real gears.

A small example would be the unless keyword. It completely throws me of each time I come across it, because it reverses the order of evaluation:

Do something, unless condition applies.

I read that from left to right, so in my mind "Do something" has already executed, but then I have to go back, because the condition might not apply. This get really 'fun' if the condition is something negative.

I like Go just because it way more simple. Even it is a bit more verbose in the error handling, everywhere else it is very minimal and clear.

Re: Exploring Error Handling Patterns in Go

#33
post #25
post #21

Earlier quoted context omitted.

In my experience, this just becomes arbitrarily close to "re-implement your stack trace by hand with space-delimited words instead of camelCaseFunctionNamesOrWhatever". I'll overwhelmingly prefer an always-correct stacktrace over a hand-recreated one that sometimes collapses multiple branches into a single ambiguous on. At least then the devs can help me when it fails. And stack traces and concatenated strings are in…

Just using WithStack() from "github.com/pkg/errors" on any error that originates from outside my repository has been my go-to rule for any Go project. It has never disappointed.

Like an exception?

Re: Exploring Error Handling Patterns in Go

#34
What is not covered here, and what I'm still searching for a good pattern for, is being able to return different errors depending on the type of failure.

Suppose you have a function that fetches a model from your database. It can return an error if the given user doesn't have permission to fetch this model, or it can return an error if your db connection barfs for some reason. The calling function needs to be able to differentiate between the two errors. Most of what I've read on the subject makes it seem like people prefer to only ever check if err != nil.

The two options I've seen in the wild are:

1. Create a constant for a given error, like:

  var ErrFetchForbidden = errors.New("FETCH_FORBIDDEN")
Then the calling function can do:

  if err == ErrFetchForbidden {
    return 403
  } else if err == ErrFetchNotFound {
    return 404
  } else {
    return 500
  }
2. Create a custom type for your error like so:

  type ErrFetchForbidden string
this has the benefit that the errorer can put more specific info into the error besides the Error() string.

  var err ErrFetchForbidden = "error retrieving the user object"
  return err
and then the caller can switch on type

  switch v := err.(type) {
    case ErrFetchForbidden:
      return 403
    case ErrFetchNotFound:
      return 404
    default:
      return 500
  }
We've gone with option 2 for now, (wrapping them with the pkg/errors package) because it seems simpler. Anyone else have good patterns for handling this?

Re: Exploring Error Handling Patterns in Go

#35
post #26
post #23

Earlier quoted context omitted.

Exceptions. Anders give an interview in 2003 [1] where he talks about how C# looked to learn from Java's checked exceptions. His conclusion was basically that, in their evaluation, 9/10 exceptions cannot be handled beyond some generic top-level handler. If this observation is correct, and it certainly aligns perfectly with my own, then bubbling makes a lot more sense. Note that, with error return values, you can emul…

I've always been annoyed by the parallel control flow introduced by exceptions in any language. They are used so often in many languages where it doesn't feel necessary. The fact that I don't even have to think if the function call I'm looking at can throw and if I should catch it or not outweighs everything.

Easy, just assume it throws. That's the case anyway. Thanks to panics, even in Go.

Edit: Also, there is no parallel control flow. Languages with exceptions have union-type return values, and every statement is implicitly followed by the equivalent of: if err!=nil return nil, err. The fact that in Go you have to type that makes Go cumbersome, not smart.

Re: Exploring Error Handling Patterns in Go

#37
post #6
post #3

Earlier quoted context omitted.

That's by design -- programmer's choice.

I don't understand. Go errors out on unused imports, but you can type "import _ foo.com/unused-import" to not error out. Why doesn't 'errors.New("asdf")' error out and require you to instead write '_ = errors.New("asdf")' to ignore the result I think the real answer is not that it's intentional design, but rather that the original compiler was not powerful enough to implement that feature easily... and once go hit 1.…

I don't think it's because of the complexity. I'm quite sure it would not have been difficult to do.

One of the reason is that you don't always want to check the error. The most common one is fmt.Println.

I would not like to always write

_, _ = fmt.Println("Hello, playground")

Re: Exploring Error Handling Patterns in Go

#38
post #34

What is not covered here, and what I'm still searching for a good pattern for, is being able to return different errors depending on the type of failure. Suppose you have a function that fetches a model from your database. It can return an error if the given user doesn't have permission to fetch this model, or it can return an error if your db connection barfs for some reason. The calling function needs to be able to…

There's another one I often use:

Create a custom error type, for example DB Error:

  type DBError struct {
     Temporary bool
     NetworkBased bool
     Cause error
  }

Now you can provide functions like IsTemporary(err).

Otherwise, you can use 2# with a twist, instead of matching on a type, you can do:

  switch {
     case isErrFetchForbidden(err):
     case isErrFetchNotFound(err):
  }
or even:

  IsBadRequest(err)
  IsInternal(err)
  IsTimeout(err)

Re: Exploring Error Handling Patterns in Go

#39
post #35
post #26

Earlier quoted context omitted.

I've always been annoyed by the parallel control flow introduced by exceptions in any language. They are used so often in many languages where it doesn't feel necessary. The fact that I don't even have to think if the function call I'm looking at can throw and if I should catch it or not outweighs everything.

Easy, just assume it throws. That's the case anyway. Thanks to panics, even in Go. Edit: Also, there is no parallel control flow. Languages with exceptions have union-type return values, and every statement is implicitly followed by the equivalent of: if err!=nil return nil, err. The fact that in Go you have to type that makes Go cumbersome, not smart.

Not really, in all the years I've been writing Go, only one library used panics for error handling.

Usually if something panics you don't want to handle it. (Other than at the http handler level, where you can just throw an InternalServerError and log the panic)

First and foremost, you can usually assume libraries won't panic, though it would be nice to have a tool (grep) to check for explicit panics.

Re: Exploring Error Handling Patterns in Go

#40
post #5

Earlier quoted context omitted.

And it’s—bluntly—a terrible design.

Why? I am genuinely curious. Terrible compared to what?

I actually really like Go error handling (I write Go daily), but truth be said, they're a poor mans Either Monad.
Post reply on HN