4 ms·
Sorry, but Rust's error handling is pretty clumsy compared to what you can do in some languages. Example: You write a function that calls 2 thirdparty librarie
by valenterry 4y ago
Sorry, but Rust's error handling is pretty clumsy compared to what you can do in some languages.
Example:
You write a function that calls 2 thirdparty libraries, both of which can fail. The typesystem in Rust is unable to express that the resulting error with be libAError or libBError. It is lacking anonymous union-types. Even if union-types have been added, you'd have to define one first and you'd have to use unsafe (at least from my understanding).
This also impacts user-defined error-types of course, but it makes errorhandling when using other libraries very annoying. You always have to define new types and do wrapping.
Another example:
You have a function that calls 2 thirdparty libraries, both of which can fail. The two libraries have decided to use a customized error-type (and not the built-in one) because they need to carry around some extra context or need some other feature, but they still support everything that Result does but in a slightly. Now you need to manually convert those or learn the interface of this specific error-type, because there is no way to abstract over different "Result"-types. Why? Because Rust has no support for Higher Kinded Types, which would be needed to do so.
There are more examples, but these are two that are immediately relevant to pretty everyone using Rust. And yes, Rust has done a lot of things right and especially better than C or C++. But when looking at it as someone who has used other high-level-languages I can say it definitely did not "absolutely nail" it.
- scaredginger 4y agoI agree with your first point, but it's worth noting that if someone is willing to pay a small runtime cost, they can just return a Box<dyn Error> or anyhow::Error in these cases ergonomically. Additionally, there is the thiserror crate which makes defining the union type you described trivial. Your second point is dubious; I've never seen any crate use anything other than Result. Afaik, it works everywhere and is so idiomatic that if somebody suggested reimplementing it, I'd immediately question their credentials.
- valenterry 4y ago> Box<dyn Error> or anyhow::Error in these cases ergonomically This comes at the cost of losing the precise information what error-types could occur and makes it harder to read the code compared to a language that can use its typesystem to model that. > Your second point is dubious; I've never seen any crate use anything other than Result. Maybe. And you also barely see something like Result being used in Python. Is that because Result is bad? No. It is because it is _hard_ to use Result in Python, both of the lack of ergonomy compared to e.g. exceptions and also because it's not standard and people will look at you funny. So why is pretty much everyone in Rust using Result? Because using your own error-type causes exactly the problems that I mentioned (and more). So no matter how you look at it - this is a shortcoming of Rust and hopefully something that the Rust team will improve in the future. (E.g. https://github.com/rust-lang/rfcs/issues/324 https://github.com/rust-lang/rfcs/issues/324)
- super_flanker 4y ago> Sorry, but Rust's error handling is pretty clumsy compared to what you can do in some languages. I agree with clumsy part, if that means error handing being more explicit. I've personally never faced 2nd example, so I'll limit my discussion to 1st one. If you don't want to use an extra crate (anyhow/thiserror), you could always do a `map_err` which in my opinion is a superior choice as you get to know what kind of error you're dealing with and how to map it to your domain error.
- valenterry 4y ago> I agree with clumsy part, if that means error handing being more explicit. Yeah no, that is not what I meant. It is possible to make error-handling even more ergonomic while staying fully explicit. I agree that implicit/magical/runtime error-handling is not more ergonomic. > If you don't want to use an extra crate (anyhow/thiserror), you could always do a `map_err` which in my opinion is a superior choice as you get to know what kind of error you're dealing with and how to map it to your domain error. I briefly looked at those crates, but while they make some parts better, they make others (for instance the explicitness) worse. On top of that, they are built using macros, which is totally okay from the perspective of the library and comes at a cost. Overall it would simply be better if Rust's typesystem were capable enough so that those macros were not even needed. All of that being said, Rust's error-handling is still good. I'm just bothered by the "Rust nailed it perfectly" attitude. It's better to be aware that there are even better solutions out there, so that we can go and improve Rust even more.
- dc01 4y agoSmall nitpick : In Rust you would use an Enum (a discriminated Union) for this not an Union and you wouldn't need unsafe.
- valenterry 4y agoThat's exactly my point: you should not have to define and use an enum here. The compiler should be smart enough to figure out that the result will be `Result<T, Error1 | Error2>` without the user doing anything here.