Re:Gopher Wrangling. Effective error handling in Go
by simiones on Tuesday June 20, 2023 @11:18AM
(#36405245)
Unfortunately, fmt.Errorf makes errors.Is/As useless. In fact, errors.Is is mostly useless in general, since very few Go libraries have any error types at all. You're usually stuck with parsing error messages if you actually want to handle errors programmatically, even for much of the standard library.
[ Parent
| on HN ]
Re:Gopher Wrangling. Effective error handling in Go
by philosopher1234 on Tuesday June 20, 2023 @12:28PM
(#36406525)
Uh? If you use `%w` in fmt.Errorf(), it should still work with .Is and .As?
Re:Gopher Wrangling. Effective error handling in Go
by pkd on Tuesday June 20, 2023 @05:42PM
(#36411084)
> Uh? If you use `%w` in fmt.Errorf(), it should still work with .Is and .As?
You still need a stable error to compare against in errors.Is. fmt.Errorf is not going to provide that if the message is dynamic - which is almost always.
Re:Gopher Wrangling. Effective error handling in Go
by throwaway894345 on Wednesday June 21, 2023 @02:27PM
(#36422355)
I think what you're talking about with "stable" and "dynamic" are error constants (like `io.EOF`) and instances of error types (like `url.Error` https://pkg.go.dev/net/url#Error). You use `Is` for the former and `As` for the latter; fmt.Errorf's %w directive doesn't inhibit either case.
Re:Gopher Wrangling. Effective error handling in Go
by simiones on Thursday June 22, 2023 @05:22AM
(#36429730)
The problem is that the first-level error in most Go functions is fmt.Errorf(some error message, some args). The idea of pre-allocating an exported global error object (like io.EOF) is very rarely useful, and error structs are very rarely used either, because they require much more ceremony than just returning a fmt.Errorf() (or even an errors.New()) on the spot. But errors.Is is only useful if you take the first option, and errors.As is only useful if you take the second option.
So, in practice, neither errors.Is nor errors.As are terribly useful, and %w just gives you a false sense of usefulness.
Re:Gopher Wrangling. Effective error handling in Go
by throwaway894345 on Thursday June 22, 2023 @09:56PM
(#36441891)
This isn’t my experience. Most popular libraries seem to give an error type or value at least when someone might reasonably want to match on it. But yeah, if your coworkers don’t do this then Go can’t stop them (and in any case, I don’t see how fmt.Errorf() is to blame).
Re:Gopher Wrangling. Effective error handling in Go
by simiones on Wednesday June 21, 2023 @04:53AM
(#36416084)
If your original error is `fmt.Errorf("failed to frobulate")` or even `errors.New("failed to frobulate")`, even if it's bubbled up with `fmt.Errorf("foo() failed: %w", err)`, errors.Is and errors.As are useless for checking it (each error in the chain, including the original, will be a new instance of fmt.stringError or something like that).
This is equivalent to the problem of throwing new Exception("some message") or new RuntimeException("some message") in Java, to be fair. But this is more rarely done there in my experience, especially because people are more used to defining new exception types since the stdlib has many examples of that, unlike Go (which all returns `error` for all error types).
Re:Gopher Wrangling. Effective error handling in Go
by philosopher1234 on Wednesday June 21, 2023 @01:11PM
(#36421368)
You are wrong: https://go.dev/play/p/6PTQk0xWAAl
Re:Gopher Wrangling. Effective error handling in Go
by simiones on Thursday June 22, 2023 @05:17AM
(#36429708)
That's not how people commonly use errors, with global variables.
The way code usually looks like is this:
func foo(arg int) error {
if arg < 0 {
return fmt.Errorf("expected >0, got %d", arg)
}
if arg > 8 {
return fmt.Errorf("expected >8, got %d", arg)
}
//do stuff
}
or func foo(arg int) error {
if arg < 0 {
return errors.New(fmt.Sprintf("expected >0, got %d", arg))
}
if arg > 8 {
return errors.New(fmt.Sprintf("expected >8, got %d", arg))
}
//do stuff
}
In either of these cases, a caller of foo() can at best parse err.String() to see why foo() complained. But there is no way that errors.As or errors.Is help with the majority of errors returned by Go programs.
Re:Gopher Wrangling. Effective error handling in Go
by Ajnasz on Thursday June 22, 2023 @08:47AM
(#36431312)
I guess it's the same in most of languages.
If you raise a general exception with a custom message you'll have to check the message not it's type.
Re:Gopher Wrangling. Effective error handling in Go
by throwaway894345 on Thursday June 22, 2023 @09:54PM
(#36441877)
That’s not the fault of fmt.Errorf(); if you aren’t giving people a type or a value to compare against there’s nothing the standard library can do to help you. You need to try string matching or maybe reflection. The standard library can’t fix bad code. Thankfully I don’t share your experience with people commonly returning those kinds of errors (at least not where one needs to match against an error).
Re:Gopher Wrangling. Effective error handling in Go
by throwaway894345 on Wednesday June 21, 2023 @02:34PM
(#36422452)
As others have mentioned, this isn't true. `errors.Is` is for named error instances (like `io.EOF`) while `errors.As` is for matching against a particular type of error (like `net/url.Error`). `fmt.Errorf()` doesn't inhibit either case.