4 ms·
err.(type) fails when errors are wrapped, which is common and needs to be accommodated. To do what you're trying to do, you need to use errors.As.
by kiitos 1y ago
err.(type) fails when errors are wrapped, which is common and needs to be accommodated. To do what you're trying to do, you need to use errors.As.
- atombender 1y agoSure. It was not intended to be a complete example.
- kiitos 1y agoIt's not that the example is incomplete, it's that it's incorrect! :)
- atombender 1y agoNo, it's just incomplete. The same code just needs to unwrap: for err != nil { switch e := err.(type) { case UserNotFound: writeJSONResponse(w, 404, "User not found") return case interface { Timeout() bool }: if e.Timeout() { writeJSONResponse(w, 503, "Timeout") return } } err = errors.Unwrap(err) }
- kiitos 1y ago`err.(type)` is incorrect, at least in general. Calling `errors.Unwrap` in application code like this is almost always a red flag indicating a design error. And in this case it definitely is!
- atombender 1y agoThat is literally what errors.As() does, finding a value up the cause chain that can be coerced to the target type.
- kiitos 1y agoIf you're re-implementing errors.As's unwrapping behavior in your application code in order to parse/evaluate errors, that's a mistake and a design error. You'd never call Unwrap outside of a custom error type's method set, and even then you'd never use a `for` loop like you're doing in your example. > it means you can target types with precision in the API layer The only situation where you need to get precise error types is when you need to provide specific details from those specific types to the consumer, which is rare. And even in those rare cases, user code does that work via errors.As, not this manual Unwrap loop process you're suggesting here.
- atombender 1y agoThat is just your opinion. There is absolutely nothing wrong with doing so, and there is nothing in the documentation that asserts what you claim. The documentation is clear that comparing an error value or casting it without following the Unwrap() chain is only an antipattern because it would not work with wrapped errors. Is() and As() are merely convenience functions, and the documentation is clear that all they're doing is calling Unwrap(), which you can do yourself.
- kiitos 1y agoIt literally is not. Is and As are not merely convenience functions, they're canonical and conventional expectations of Go code, if you're doing that work yourself you're almost certainly doing it wrong.
- atombender 1y ago> The only situation where you need to get precise error types is when you need to provide specific details from those specific types to the consumer, which is rare. It's not rare in my experience. All they apps I work on have a central unhandled error handler in the API that converts Go errors to HTTP or gRPC error responses, and then falls back to a general "internal error" if no specific error could be mapped. I can think of many other instances where we have a switch over half a dozen error typed in order to translate them into other types across RPC or pub/sub boundaries. > And even in those rare cases, user code does that work via errors.As, not this manual Unwrap loop process you're suggesting here. As() does not work with switch statements unless you pre-declare a ton (in our case, often a couple of dozen) error variables. Secondly, it is deeply inefficient. As() traverses the cause tree recursively for every single error, so if you have 30 possible error types to compare, and an error typically wraps 3 layers deep, that's a worst case of 30 loop iterations with 90 cases, as opposed to my method, which is 3 loops.