News for hackers. Stuff that matters.
Re:What's New in Python 3.11? by kortex on Monday June 27, 2022 @09:36AM (#31894420)
Rust absolutely nails the error handling department.

Python style exceptions, pros:

- rapidly drop through layers when needed to get to a stable entry point - easy to add errors deep in the stack

Cons:

- basically a limited GOTO, therefore side-effect, therefore not composable - no way to know if what you are calling is gonna throw on you

Java pros:

- have to declare exception types, so at least you know what you are dealing with

Cons:

- you have to always declare exception types, which can be tedious - same non-composable GOTO-lite behavior - adding an exception deep in the stack is tricky

Go pros:

- errors are values, therefore typed and technically composable - no surprises

Cons:

- tedious, verbose - can't rapidly unwind unless you panic - rarely composable in practice, requires manual unwrapping in most cases - adding an exception deep in the stack is tricky

Rust Result pros:

- strongly, statically typed - no surprises - higly composable - can map over it - rapidly unwind in a composable way with ? operator

Cons:

- adding an exception deep in the stack is tricky (but often amenable to programmatic refactoring)

It's no wonder Rust is the SO most loved language 7 years running.

Python actually has a Result type library which I really like, but it's been hard selling my team, and you really need buy in. But I'd give it a swing.

https://pypi.org/project/result/

Re:What's New in Python 3.11? by valenterry on Monday June 27, 2022 @10:17AM (#31894907)
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.

Re:What's New in Python 3.11? by scaredginger on Monday June 27, 2022 @10:49AM (#31895333)
I 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.

Re:What's New in Python 3.11? by valenterry on Monday June 27, 2022 @02:52PM (#31898998)
> 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)

Re:What's New in Python 3.11? by super_flanker on Monday June 27, 2022 @02:34PM (#31898731)
> 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.

Re:What's New in Python 3.11? by valenterry on Monday June 27, 2022 @02:48PM (#31898934)
> 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.

Re:What's New in Python 3.11? by dc01 on Tuesday June 28, 2022 @07:04AM (#31906237)
Small nitpick :

In Rust you would use an Enum (a discriminated Union) for this not an Union and you wouldn't need unsafe.

Re:What's New in Python 3.11? by valenterry on Tuesday June 28, 2022 @07:16AM (#31906303)
That'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.