3 ms·
I've found that Go has significantly more mental overhead when it comes to error handling than rust does. In Go, I don't have type information to know what kin
by TheDong 5y ago
I've found that Go has significantly more mental overhead when it comes to error handling than rust does.
In Go, I don't have type information to know what kind of error it is. In rust, I can typically just "match err", and let the compiler tell me what error types it could be, and then handle each of them.
In go, I always have to read the entire function that returns the error, and any functions it calls, and so on, and then I have to think about how to handle it.
Is it an error from the os package, like "os.Open"? Well, maybe I need:
if os.IsNotExist(err) { /* handle */ }
Is it an error from the net package? I might need:
var addrErr *net.AddrError
var invalidAddrErr net.InvalidAddrError
var otherErr net.Error
if errors.As(err, &addrErr) {
/* handle addrErr */
} else if errors.As(err, &invalidAddrErr) {
/* handle invalidAddrErr */
} else if errors.As(err, &otherErr) {
/* some other net error */
} else if errors.Is(err, net.ErrClosed) {
/* handle this network error which is _not_ a net.Error */
}
Is it an error that used one of the third party error libraries, like "github.com/hashicorp/errwrap"? I may need even more special handling.
The fact that "errors.Is" and "errors.As" requires me to know if an error is a "sentinel" one like 'net.ErrClosed', or a struct error, like 'os.PathError', and I have to know if the struct is implementing error on a pointer receiver or not is also really annoying since it means I have to constantly go check error definitions, even if I vaguely remember what errors a function returns.
And, what's worse, the type system doesn't help me! It won't tell me if I get any of these things wrong. I can use "errors.Is" and "errors.As" backwards, and the type system won't help. I have no way to determine what errors a function can return without reading hundreds of lines of code.
Frankly, Go's error handling has more overhead than almost any other language, and I find it surprising you find it simple.
If all I'm doing is bubbling errors up, then rust's "?" operator works fine, and is easier than Go's "if err != nil" boilerplate. If I'm handling errors, rust's features are helpful, and Go is so much mental overhead it usually makes me forget the problem I was trying to solve by the time I've drawn out the full diagram of possible errors something can return up the stack. Usually rust's type-system has enough info that I don't need to read any code to know what errors I need to handle. That's never the case in Go, because idiomatically errors are "type-erased" into the error interface.
Oh, and this is all not to mention that go facilitates making unhandle-able errors. If someone returns "errors.New" or "fmt.Errorf", you're stuck with string matching, while rust encourages making typed and handle-able errors. The number of times I've had my go code break because someone reworded an un-exported error string I had to match on is pretty high. Hasn't happened to me in rust yet.
- sa46 5y agoNot a rust aficionado: If you’re using error.As, wouldn’t you need essentially the same logic in Rust to match the error type and get access to the right fields? Seems like the big difference is the compiler knows the variants of the error. The trade off is that Rust modules are tightly coupled by the error type since you have wrap each one into the module’s own error type.