Earlier quoted context omitted.
> e.g. net.OpError I have my own complain against Rust's error handlings, but I can't bring myself to praise what Go has been doing. One problem with the error interface is that you can't know exactly which error type will be returned, and the Go authors may add new error types form time to time. `net.OpError` itself is a great example for that, which is a newer type than net.Error. This style of error signalling is…
> This style of error signalling is best used when the error handling is binary, > either "No error, continue" or "Errored out, abort", but not branched > out path like "If encountered error A, do this. If encountered error B, > do that. Otherwise, abort". If I'm understanding you correctly, you can implement your example in a fully idiomatic way, and the stdlib does this in a bunch of places. Errors are just values,…
I don't think you understood my point. Instead, I believe you launched your argument too quickly. Because if you continue reading, you'll see:
> ... but if you want to so the same in Go, there will be a nightmarish manual type discovery and tracking operation waiting for you.
Which should deter you from providing the example where you declared the `ErrFoo` and `ErrBar` type, since it's basically the "nightmarish manual type discovery" scenario unfolding in basically realtime.
But let's continue with the example to make my argument more full:
Let's say one day the `ErrFoo` and `ErrBar` type is not sufficient, so you declares another type `Err2000`. Now what happens down stream?
Since the some function may just return an error interface, there's no way for the end user to know you've added that error type. From their prospective, everything is unchanged, and still compiles just fine.
If the user's code is relying on the exact error type for branching, then they have to perform "manual type discovery and tracking" to monitor the changes to your code (and everything above it, really) to ensure that the branching is still done correctly.
In Rust however, since error is a emun, all error conditions must be handled or explicitly ignored during a `match`, if you write something like
match File::open("filename.txt") {
Ok(file) => {}
Err(io_err) => match io_err.kind() {
ErrorKind::IsADirectory => {} // Notice how not every error type is handled
},
}
The compiler will just stand up and make your ass weep. Meaning if you add some new error conditions, your user will know that for sure when they compile.(BTW, `std::io::ErrorKind` is indeed `non_exhaustive`, but you the maker of the error type still have to explicitly/intentionally declare it to make it non_exhaustive)
So, let's read my comment again:
> Rust's error handling encourages such branched out handlings by default, but if you want to so the same in Go, there will be a nightmarish manual type discovery and tracking operation waiting for you.
(Also, "by default" is there to indicate that yeah you can boxing a `std::error:Error` trait in Rust, then you ended up with something like how Go handles error. I know.)