6 ms·
Rust error handling can be concise thanks to the try! macro, but macros bring their own problems (like making more difficult to write refactoring and static ana
by ngrilly 11y ago
Rust error handling can be concise thanks to the try! macro, but macros bring their own problems (like making more difficult to write refactoring and static analysis tools).
Haskell error handling can be concise thanks to monads, but they need higher kinded types which bring their own share of complexity.
The conversation on the "RFC: Stabilize catch_panic", found on Rust's issue tracker, illustrate some unsettled questions I had in mind (https://github.com/rust-lang/rfcs/pull/1236 https://github.com/rust-lang/rfcs/pull/1236).
For example, kentonv wrote:
All code can fail, because all code can have bugs. Obviously, we don't want every single function everywhere to return Result<T, E> as a way to signal arbitrary "I had a bug" failures. This is what panic is for.
graydon wrote:
Currently you've adopted a somewhat-clunky error-type with manual (macro-assisted) propagation. Some rust code uses that correctly; but much I see in the wild simply calls unwrap() and accepts that a failure there is fatal.
ArtemGr wrote:
The only way to maintain both the safety and the no-panic invariants is to remove the panics from the language whatsoever. Explicit errors on bounds check. No assertions (you should make the assertion errors a part of the function interface instead, e.g. Result). Out of memory errors returned explicitly from every heap and stack allocation.
If you'd like to keep the assertions, the smooth allocations and other goodies then you either need a way to catch the panics or end up making programs that are less reliable than C. No modern language crashes the entire program on an out-of-memory or an integer overflow, but Rust will.
The libraries we have, they do panic, it's a matter of fact. Withing the practical constraints and without some way of catching panics you can't make a reliable program that uses external crates freely.
BurntSushi wrote:
If something like catch_panic is not stabilized, what alternative would you propose? (Option<T> and Result<T, E> are insufficient.)
On the same topic, there is this post about introducing a `?` operator or a `do` notation (inspired by Haskell) to streamline error handling:
http://m4rw3r.github.io/rust-questionmark-operator/ http://m4rw3r.github.io/rust-questionmark-operator/
And there is RFC 243, about "First-class error handling with `?` and `catch`":
https://github.com/rust-lang/rfcs/pull/243 https://github.com/rust-lang/rfcs/pull/243
But I'm sure you're quite aware of these discussions :-)
My general feeling is that, whatever programming language you consider (Python, JavaScript/Node, Go, Rust, Haskell, Erlang, etc.), the right way to handle errors is still an open question.
- pcwalton 11y ago> Rust error handling can be concise thanks to the try! macro, but macros bring their own problems (like making more difficult to write refactoring and static analysis tools). No, it's not more difficult to write static analysis tools. You use libsyntax as a library. Refactoring tools, maybe, but it's a lot better than refactoring with code generation :) > For example, kentonv wrote: How does that describe an unsolved problem? It illustrates that Rust's bifurcation of errors into Result and panics works. > graydon wrote: I think it's a relatively minor issue that would be solved with "?" or something like what Swift does. Switching to Go's system would make it worse; Graydon's criticism applies even more so to Go than to Rust. > ArtemGr wrote: Catching panics is important, yes. No argument there. It doesn't change the overall structure of Rust's error handling story, though.
- ngrilly 11y ago> No, it's not more difficult to write static analysis tools. I agree it's solvable, but I'd argue it's a bit more difficult to write static analysis tools when macros are implied. But maybe I'm missing something. Here is an example: The subsystem types in the sdl2 crate starting in 0.8.0 are generated by a macro, so racer has issues evaluating the type of video(). A workaround is to explicitly declare the type of renderer. (source: https://github.com/phildawes/racer/issues/337 https://github.com/phildawes/racer/issues/337) But my real concern is how to write refactoring tools when macros are implied. It's seems a lot harder than writing static analysis tools, because the refactoring tool wants to examine the source code with macros expanded, but has to modify the source code with macros unexpanded. In other words, the tool has to map from source with expanded macros, back to source with unexpanded macros. How do you solve that? As a sidenote, I agree that refactoring generated code doesn't sound fun too :-) > How does that describe an unsolved problem? It illustrates that Rust's bifurcation of errors into Result and panics works. I quoted kentonv here because it shows that Rust and Go have converged towards structurally similar solutions to error handling, by using two complementary mechanisms: explicit error checking one one hand (using Result<T,E> in Rust and using multiple return values in Go) and panic/recover on the other hand. The big difference is that Rust have sum types (instead of using multiple return values in Go) and macros (try! instead of repeating `if err != nil { return err }` in Go). > Catching panics is important, yes. No argument there. It doesn't change the overall structure of Rust's error handling story, though. You're right.