3 ms·
Why? This doesn't look particularly more or less friendly in Rust than in any other programming language: https://doc.rust-lang.org/book/ch09-00-error-handling
by danesparza 3y ago
Why? This doesn't look particularly more or less friendly in Rust than in any other programming language: https://doc.rust-lang.org/book/ch09-00-error-handling.html https://doc.rust-lang.org/book/ch09-00-error-handling.html
- LelouBil 3y agoA lot of common error handling patterns are expressed more effecively in Rust. For bubbling up errors you have the ? operator, then on results you have map, map_err, and, ok_and...
- ColonelPhantom 3y agoAside from the other reply which points out that common idioms have useful shorthands like ?, Rust's type system is also error-aware. Rust errors are actual sum types (either Ok(T) or Err(E)), while Go just uses multiple return/tuples to return (value, nil) or (nil, error). This means that in Rust, you are forced to handle errors (well, unless you don't care about the return value, but then the compiler still gives a warning). This means it is harder to make a mistake with error checking.
- monocasa 3y agoIt also enforces the lack of weird anti patterns like some functions in go that will return a significant value in the first return value while also returning non nil in the error, with a snarky comment in the docs about why this actually makes sense in this case.
- beautron 3y agoI don't consider this an anti-pattern, but part of the flexibility gained by Go's lightweight approach to error handling. I believe Go was even designed with this use case in mind (the standard library uses it in many places, and I don't consider their documentation about it "snarky"). Sometimes you want to return a partial result along with an error. Go's idiom of returning multiple values, with the final value being an error, allows this situation to be easily supported.
- monocasa 3y agoExcept in the case I'm talking about, there was a resource to be reclaimed, the GC didn't cover it. And if you wanted to do that in rust, that's a valid thing too, either as using a tuple instead of a sum type, or a tuple in the Err side of a result. You can describe what you're trying to do simply with the function signature.
- lmm 3y ago> Sometimes you want to return a partial result along with an error. Sometimes you do, and there are types that represents that. Having a single type that is almost always used a certain way but occasionally used subtly differently is a trap waiting to bite you.
- beautron 3y agoI've been using Go for almost a decade, and I don't recall this ever biting me. It doesn't feel like a trap. Go has a convention of documenting each function with a comment (which is adhered to by the standard library, my own code, and any other code I'd consider worthy of depending on). So when I think this subtlety matters, I check the documentation. Usually I don't care either way: When I get a non-nil error, I typically don't care about the other result (whether partial or the zero value). The distinction rarely matters in practice (and when it does, the documentation is there). I think this simplicity is the right tradeoff (vs. being burdened with more types to think about).
- lmm 3y agoHow is a type more of a burden than documentation? You're saying that consistent documentation is a good thing, but types enforce more consistency and integrate better with tools.
- beautron 3y agoDocumentation has immense value beyond just this specific error situation. Its a burden worth taking on regardless of how errors are handled. So it's not that a type is more of a burden than documentation, but rather that a type is an additional burden (since we're taking on the documentation burden either way). I like that Go's error handling is simple enough that I can keep all its rules in my head. And I like that the other parts of Go are simple like that too. It allows me to easily know exactly what is going on at the language level (while my attention is focused on higher levels).
- masklinn 3y ago> Rust's type system is also error-aware. Rust is actually largely error-unaware. It does have some syntactic sugar (mostly `?`, and even then that’s not restricted to errors), but for the most part Result just an enum with a `must_use` annotation, everything flows down from that.
- rtpg 3y agoThe question-mark shorthand (?) basically does what the blog post is describing, and makes a lot of this stuff straightforward. There _is_ some futzing that sometimes have to happen due to various operations' Result types using incompatible Error types. That's just a thing that has to be dealt with.
- mcronce 3y agoYeah, but that futzing typically happens separately near the "outer" error type's declaration - and when it doesn't, a concise .map_err() often does the job. It's also often taken care of by a sort thiserror macro invocation per "inner" type. There are obviously more complex error setups, but this covers the vast majority IME
- rtpg 3y agoI get why it's like this but I have often found that most of the time code ends up bailing by using a conversion into `String` quite quickly, leading to everything being stringified anyways. I get why this is, but it does make me miss the Python Exception model of "there's ~15 base exception types. One of them is probably good enough for you". One could point out that the arguments are usually "just" strings there too, but at least there's some conventions. I understand Rust's philosophy, I just find it annoying.
- mcronce 3y agoI haven't experienced that at all. Any of the codebases I've worked in - both proprietary and open-source - have converted errors to strings when it becomes necessary: When presenting it to a user, whether that means logging or placing it in an HTTP response body, etc.
- _AzMoo 3y agoI find Rust's question mark shorthand and the resulting futzing you're talking about adds a lot more cognitive load for me than Go's verbose if err conventions. I think more than anything this kind of syntactical discussion is just very subjective.