3 ms·
The try macro is going to be replaced by cleaner syntax soon (along with control flow based catch syntax). It's not "a layer of macros", its one macro, which ev
by Manishearth 11y ago
The try macro is going to be replaced by cleaner syntax soon (along with control flow based catch syntax). It's not "a layer of macros", its one macro, which everyone knows about, so it's not invisible. No different from a return or throw statement -- the control flow escape hatch is "invisible" there, too, but everyone knows what a return/throw are, so it's perfectly visible. It's the same situation with try -- everyone knows what it does; so it's not invisible.
Rust doesn't encourage writing things monadically. You can write them as nested if lets if you want; indeed; many people do exactly that (I prefer doing this too, or using try).
Where's the wallpapering? There's try!, and a couple of monadic methods on Result, and that's about it? Monadic error handling is not a new idea, and it's not really complicated either (well, if you force people to understand monads first, it is, but that's totally unnecessary and nobody does that). This is no more complicated than vanilla C++ exceptions. It's different, and different from what people are used to, but not new.
Also, this isn't even part of the language, it's part of the stdlib. If anything that is a point for Rust, since C++ needs language integration for exception handling, Rust doesn't (and thus the language is simpler in this axis). It will soon become a part of the language; but only as some sugar.
And this is a very specific example. Overall, where does "Rust start at C++s complexity level"?
> Each object gets to define its own control structure syntax.
Technically only Result and Option do, the objects above are just Results. While you can create your own enums for error handling, most people don't, so there's no repetition of control flow syntax.
This is true anywhere, each object always gets to define its own utility methods. You have the same on the std::exception types in C++.
> The machinery in Rust to avoid exceptions
This is not "to avoid exceptions", it shouldn't be viewed that way. Sure, the Rust designers don't want exceptions in the language, but monadic error handling is a proper, tried-and-tested solution for error handling, not a "last resort".
- cousin_it 11y agoFor what it's worth, I agree with Animats that Rust's error handling is too complicated. My dream language would have catchable, unchecked, untyped exceptions. I.e. you can throw and catch strings anywhere without declaring that upfront. You can even throw from a destructor while unwinding, whereupon the new string gets appended to the old string and life goes on. It's a pretty sweet design: 1) Easy to read code with standard control constructs. 2) No performance overhead in the common case. 3) No dispatching on error types, therefore less temptation to use errors for control flow. 4) No distinction between recoverable errors (option) and unrecoverable errors (panic). I feel that distinction is in the eye of the beholder, especially if you need to catch errors from code you don't control. 5) No distinction between code that can cause errors and code that cannot, thus higher-order functions become easier to write. I'm hard pressed to name any advantages of Rust's model compared to the above. Rust can't even claim to be transparently callable from languages that don't support exceptions, because panics exist :-(
- Manishearth 11y ago> It's a pretty sweet design Yeah, it is, but it wouldn't interact well with Rust's safety guarantees :) (You're analysing this feature in a vacuum, but that's not how language design is done.) > Easy to read code with standard control constructs. I think this is in the eye of the beholder, `try!` or `?` or `catch` are (would be -- for the latter two) easy to read for Rust folks. It's just different. do-notation in Haskell is similarly "standard" for functional types, but it's totally alien to C++ folks. The main reason folks find try/catch/throw easier to understand is because they're used to it. Once I learned how enums worked in Rust, `Result` was a very straightforward and simple thing. I actually personally find `try!` easier to read, since I know exactly where a return can happen, unlike C++ where the control flow is totally obscured. This leads to the rug getting pulled out from underneath you, which can be bad for safety. In fact, Rust's `recover()` needs to be careful about types allowed to cross a recover boundary so that it can be 100% memory safe. So the code might be easy to read, but not easy to reason about. > No dispatching on error types, therefore less temptation to use errors for control flow. I don't get what you mean here. Rust doesn't dispatch on error types, though it does dispatch on Result (which may contain an error). I don't see what's wrong with this. This lets you program in a functional way (and like I mentioned, you don't have to), which isn't a bad thing. Errors _are_ a control flow thing, you can't escape that. C++ handles the control flow around errors with try, throw, and catch, Rust does it with Result and its methods (and the try macro). > especially if you need to catch errors from code you don't control. `recover()` exists. Use it sparingly; it's basically only for cases when you want to catch panics in code you don't control (even then, try other solutions), or stop panics from crossing FFI boundaries. Panics are supposed to be for irrecoverable or impossible things, where "irrecoverable" is usually a statement that only makes sense in an application, not a library. So this problem in theory shouldn't come up (I haven't seen it happen much in practice). I think the problem of forgetting to catch an exception from code you don't control because you don't know it's there is a much more pressing problem than having stray panics (since panics are relatively rare). > No distinction between code that can cause errors and code that cannot, thus higher-order functions become easier to write. Result<T,E> is also a type. Treat it as any other return type in a higher order function and it works. In `Fn(...) -> T`, `T` can be a Result type, no problem. > Rust can't even claim to be transparently callable from languages that don't support exceptions, because panics exist and so does `recover()`. > No performance overhead in the common case. IIRC it doesn't turn out to be much, but ICBW. I think someone looked into this.