5 ms·
Is that not identical to an empty catch block? I fail to see how this is materially different
by isbjorn16 6y ago
Is that not identical to an empty catch block? I fail to see how this is materially different
- Fellshard 6y agoI believe the difference is that you resolve the error at the same point where you resolve a successful result, making it impossible for a reader of the function to, for example, assume that you can access a value after an exception was thrown. MyType x = null; try { x = doRiskyThing(); x.doThing(); } catch(CheckedException e) { ... } x.doThing(); This code makes it unclear where the exception is sourced from, and makes it seem like you can use `x` even in a failure scenario. In Rust, however: match doRiskyThing() { Ok(x) => x.doThing(), Err(e) => ... } Here, it's clear where `x` is available and valid. You could even have the error path panic and merely return `x` if you wanted to use it later on in the method (borrow rules permitted, etc.) A model that fits this more closely in Java is 'try-with-resources'.
- akx 6y agoYour example smells like a failure of the type system. If that were TypeScript, for the simplest example, you'd explicitly have to type `x` as `MyType | null`, and were that the case, the `x.doThing();` outside the try block would not type-check.
- Fellshard 6y agoIt can be encoded in the type system as well. That's really what Java's checked exceptions are: one of the only places in Java where you can specify an alternative 'return' type. But it isn't quite complete enough to use in Java; little quirks here and there make it difficult to use widely, especially since Java 8 and lambdas arrived. You can still use Java's type system to get something approximating Rust's approach. One upshot of Result-based error handling is that you can often replicate it in any language by shaving off one or two of the biggest benefits to suit that language's constraints.
- isbjorn16 6y agoI don't know that I agree that you're doing an apples to apples comparison though. There's nothing to stop you from doing: let x = doRiskyThing(); match x { Ok(x) => x.doThing(), Err(e) => ... } x.get().doThing(); That would be more comparable to the try catch from above, and a comparable try catch that actually is semantically approximate would be: try { MyType x = doRiskyThing(); x.doThing(); } catch(CheckedException e) { ... } Putting these two together, it makes the Result type seem worse than a try catch, right? But that isn't fair to result, because I'm using it like a jackass. In other words, you can be a jackass using either of them, but you can also use them correctly, and when you use them correctly I don't see how they're any different. I also don't feel like the likelihood of using a Result correctly (edit: originally this said _incorrectly_) is substantially higher than a try/catch, but it probably boils down to developer experience and comfort with a particular varietal and not the capabilities inherent in either. Probably. And if not, I'm looking forward to being shown why! :)
- Fellshard 6y agoI think I see what you're getting at, but let me point out why I still think it does something more than what Java exceptions allow: let x = doRiskyThing(); match x { // Pattern match on x Ok(y) => y.doThing(), // Giving the result a different name clarifies what's happening Err(e) => ... } x.unwrap().doThing(); // You can assume it's Ok, but you explicitly risk the application panicking Of note, Result::unwrap is approximately the following: match x { Ok(y) => y Err(_) => panic!() } x is still a valid value for the duration, and is never uninitialized; and whether you handle each case in a match or unwrap, both success and failure are considered and responded to before moving on. The Java checked exception code does that as well, except that it is unclear whether `doRiskyThing()` is throwing the exception, or `x.doThing()`; it's hard to reason about where the error originates from, and how much work was partially completed. In Java, the best way to circumvent this is - if possible - to keep try-clauses as tight as possible.