Hacker News new | ask | show | jobs
by GeneralMayhem 478 days ago
One thing that `if err != nil { return err }` lets you do, which panic/recover doesn't, is annotate errors with context. If you're throwing from 5 layers deep in the call stack, and two of those layers are loops that invoke the lower layers for each element of a list, you probably really want to know which element it was that failed. At that point, you have two options:

1. Pass a context trace into every function, so that it can panic with richer meaning. That's a right pain very quickly.

2. Return errors, propagating them up the stack with more context:

  for i, x := range listOfThings {
    y, err := processThing(x)
    if err != nil {
      return fmt.Errorf("thing %d (%s) failed: %w", i, x, err)
    }
  }
3 comments

You can add arbitrary information by catching and rethrowing exceptions (which go panics, basically, are).
Go panic isn't really usable as catch/rethrow because it can only be done at function scope. To make them useful for that pattern, you need a scoped `try { }` block where you can tell what part failed and continue from there. Either that, or you need lots and lots of tiny functions to form scopes around them.
You don't need "lots and lots" of tiny functions, most of the time it's totally fine to just let the exception propagate as is. When you do need to add information, you will have to use a function, yes, it's an unfortunate feature of golang. Same with defers, the only way to scope them is to wrap them in a function, it's stupid, but this is golang for you
That’s mostly just a stacktrace. You can add other information that wouldn’t be in a stacktrace, but you shouldn’t do it by string concatenation because then every instance of the error is unique to log aggregators. Instead, you need to return a type implementing the error interface. Which is not all that different from throwing a subclass of exception.
I mean yes, but I don't really like hand writing exception tracebacks via error wrapping.

That said... I did like a clever bit I did where you can use a sentinel error to filter entire segments of the wrapped errors on prod builds. A Dev build gives full error stacks.