I think checked-exceptions as implemented in Java had a number of flaws that Rust’s corrects:
They don’t cover common exceptions, most notably NullPointerException, contributing to a feeling that they don’t add a lot of value.
Suppressing a “can never occur” exception was verbose, e.g. UnsupportedEncodingException on "utf-8". The Java spec says UTF-8 must be available, but you have to write the handful of lines of code to catch UnsupportedEncodingException anyways! In Rust the equivalent situation is handled with .unwrap() or .expect("..."), much less verbose.
If you have a function that can have multiple error conditions: say, a function that makes an HTTP request and parses a JSON response, you’ve got (at least) 3 categories of error: HTTP errors (e.g. a 404), JSON parse errors, and network IO errors. In Rust the convention would be to wrap those into an enum with three variants, and there’s a bunch of ergonomic tools for taking a Result and wrapping it into the correct one. In Java convention seems to be declaring that every function raises three different exception types, adding verbosity at every call definition.
I agree. It just saddens me that Kotlin makes all exceptions unchecked, even those coming from Java, instead of automatically wrapping the Java code in Result<T, E>.
There’s a lot of things Rust does right that no JVM language currently does well.
At least Rust has unwrap, when you know that errors should not happen if code is correct, or for initial rough code. Java’s checked exceptions are frustrating just because there’s no short syntax for re-raising as unchecked exception (and preserving stacktrace, some IDEs even add code that prints stacktrace to stderr in such unwrap-like handlers).
I hated checked exceptions right up until I tried to write some software that had to be more reliable than a http worker that got restarted every request.
Turns out that when I write to a file I really want to know exactly what can go wrong.
The difference is that results are plain old values that fit in the normal type system. You can call a higher-order function with a function that returns a result and it will just work. Checked exceptions were indeed a terrible mistake, not because they force you to handle errors, but because they were a secondary type system that didn’t interoperate properly with the primary type system.
(People who are proposing effect systems should take note)
Sure, but the solution would have been to wrap checked exceptions in a Result type for interop, not, like kotlin does today, to just swallow all of them.
the solution would have been to wrap checked exceptions in a Result type for interop
There are a couple of problems with that - performing a JVM catch at every interop boundary is inherently inefficient, and exceptions don’t quite have the nice monadic composition you’d expect from results.
Well I’ve been using Rust for about a year now, and this article really only beings to scratch the surface. Error handling in Rust is actually quite involved and requires a fair amount of study when you are new to the language.
Take a look at the section in the Rust book on error handling for an idea of what I’m talking about (the first edition of the book explains better than the second edition IMHO).
The failure crate that the author touched on claims to improve the situation, but I’ve not yet tried it.
A lot of clunky error handling is a side effect of returning data. When we call getLocation we get back the retrieved location and an error code. If we were messaging, we could simply message someone the location when we can get it, and message an entirely different part of code if we can’t get it.
It’s funny how for many years people hated on checked exceptions as the worst mistake every – and now they’re back as Result types.
I just wish this realization had come sooner, as now too many languages don’t have support for either.
I think checked-exceptions as implemented in Java had a number of flaws that Rust’s corrects:
NullPointerException, contributing to a feeling that they don’t add a lot of value.UnsupportedEncodingExceptionon"utf-8". The Java spec says UTF-8 must be available, but you have to write the handful of lines of code to catchUnsupportedEncodingExceptionanyways! In Rust the equivalent situation is handled with.unwrap()or.expect("..."), much less verbose.Resultand wrapping it into the correct one. In Java convention seems to be declaring that every function raises three different exception types, adding verbosity at every call definition.I agree. It just saddens me that Kotlin makes all exceptions unchecked, even those coming from Java, instead of automatically wrapping the Java code in Result<T, E>.
There’s a lot of things Rust does right that no JVM language currently does well.
Such as? Scala is very Rust-like; it doesn’t do linear typing but that wouldn’t help you much on the JVM anyway.
At least Rust has
unwrap, when you know that errors should not happen if code is correct, or for initial rough code. Java’s checked exceptions are frustrating just because there’s no short syntax for re-raising as unchecked exception (and preserving stacktrace, some IDEs even add code that prints stacktrace to stderr in suchunwrap-like handlers).I hated checked exceptions right up until I tried to write some software that had to be more reliable than a http worker that got restarted every request.
Turns out that when I write to a file I really want to know exactly what can go wrong.
My only experience with checked exceptions was java, and that sucked… But inferred+merged checked exceptions could be cool. Any languages have that?
If you use
ExceptTin Haskell with a polymorphic error type you’ll get this.Yes, Ocaml has it with Polymorphic variants +
resultmonad. The one current downside is the error messages can be less than ideal.A few blog posts describing it:
http://functional-orbitz.blogspot.se/2013/01/introduction-to-resultt-vs-exceptions.html
http://functional-orbitz.blogspot.se/2013/01/experiences-using-resultt-vs-exceptions.html
The difference is that results are plain old values that fit in the normal type system. You can call a higher-order function with a function that returns a result and it will just work. Checked exceptions were indeed a terrible mistake, not because they force you to handle errors, but because they were a secondary type system that didn’t interoperate properly with the primary type system.
(People who are proposing effect systems should take note)
Sure, but the solution would have been to wrap checked exceptions in a Result type for interop, not, like kotlin does today, to just swallow all of them.
There are a couple of problems with that - performing a JVM catch at every interop boundary is inherently inefficient, and exceptions don’t quite have the nice monadic composition you’d expect from results.
Well I’ve been using Rust for about a year now, and this article really only beings to scratch the surface. Error handling in Rust is actually quite involved and requires a fair amount of study when you are new to the language.
Take a look at the section in the Rust book on error handling for an idea of what I’m talking about (the first edition of the book explains better than the second edition IMHO).
The failure crate that the author touched on claims to improve the situation, but I’ve not yet tried it.
A lot of clunky error handling is a side effect of returning data. When we call getLocation we get back the retrieved location and an error code. If we were messaging, we could simply message someone the location when we can get it, and message an entirely different part of code if we can’t get it.