The method type signatures clearly specify what exceptions will be thrown.
And the compiler requires you to deliberately handle those exceptions.
But I wouldn't consider Java to be the best approach for error handling anyway.
What makes it '90s style' other than you don't like it?
Calling something "90s style" and its alternative "modern" is a thought-terminating cliche.
- it returns both a return value and the error value, not xor. The convention is to use the error value only if returned, but sometimes the other “slot” is also used. If you squint a bit, this is exactly what errno does in C. The better, ML-y or exception-y way gives you a sum type instead, which can’t be mishandled.
- you have to handle it in-place with no convenient shorthand allowing for handling it at another layer (and usually that’s the only meaningful way forward). Also, error handling is a cross-cutting concern, squeezing it inside your business logic is (in my opinion) a bad thing.
Whether or not it's better to have a sum type (IMO it isn't), what Go has isn't anything like errno. errno is an ugly wart on the POSIX interface, which is otherwise quite nice. For example, Linux system calls directly return negative error codes, which is a much better way of handling errors, and then the libc has to set errno and return -1. Awful.
>- you have to handle it in-place with no convenient shorthand allowing for handling it at another layer (and usually that’s the only meaningful way forward). Also, error handling is a cross-cutting concern, squeezing it inside your business logic is (in my opinion) a bad thing.
That you say this and then advocate for optional<T>/result<t,e> is insane. That's exactly what a sum type does: it squeezes error handling into your business logic. And no, papering over that with ugly ? operators everywhere is not a solution, it is syntactic sugar. Error handling is part of the logic of your program. It is not just some 'ah we'll return it' thing. Many operations can fail and many errors are recoverable.
Which is an implementation detail, has nothing to with its semantics what we discuss here. Certain C compilers do wild things with errno as well.
I’m not advocating for optional/result types, I personally believe that checked exceptions are the ideal solution, but it is a thoroughly underutilized tool in PL designs (Java has it, though it has many warts, but that is not inherent to the concept). Hopefully with the wake of effect-typed languages it could change.
With that said, in Rust’s low-level case it makes sense to avoid exceptions, and sum types are an okay solution, especially if it has a handful macro for bubbling up. What go has is just straight up wrong.
Monadic error handling (Rust's Option<T>/Result<T, E>) avoids that issue by putting the error inside the return type as a normal value.
(Which is why, on multiple occasions, I've seen people call Result<T, E> "checked exceptions done right/properly".)
But they fail at including a stacktrace, autobubbling up, auto-unwrap and customizable "hit radius" with a try-catch block (okay, I'm sure some Monadic construct allows for that, but e.g. Rust's version does not).
(Plus, true exception handling is messy for a language that cares so much about FFI. Look at the discussions surrounding how panic! unwinding should behave at FFI boundaries.)
2. I think completely automatic bubbling would be a misfeature, and the `?` operator is a good balance between concise ease-of-use and the Rust focus on "code is read far more often than it's written".
3. Again, the `?` operator. I think completely automatic unwrap would also be a massive footgun and comprehension hazard.
4. As mentioned in the other reply, try blocks are in development. (See my previous comment about Rust adopting a Minimum Viable Product approach to v1.0)
https://caniuse.rs/ is a great illustration of how far we've already come and how minimal Rust 1.0 was.
Some examples (not an exhaustive list):
1. Fibers/stackful coroutines (See "Fibers under the magnifying glass" by Gor Nishanov for why they were hugely popular in the 90s and then got abandoned by all the major OSes and programming languages → http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2018/p136...)
2. Garbage collection (The 90s were the height of GC hype, to the point that the JVM still struggles with "Don't worry. GC research will make it fast." design decisions.)
3. Error handling that feels like "Well, we know that exceptions were a mistake, so the only other option is C-style return values... but, hey, we can at least follow 90s Python's lead in supporting returning tuples." (Granted, exceptions DO bake in a fundamental assumption that all the world can be treated as single-threaded execution amenable to transactional rollback. → https://www.lighterra.com/papers/exceptionsharmful/)
4. No generics until they were pushed into it by things like people writing preprocessors that use the Canadian Aboriginal Syllabics characters that look like angle brackets. (https://www.reddit.com/r/rust/comments/5penft/parallelizing_...)
5. No sum types (Bear in mind that sum types have been around since ALGOL 68 and, on many fronts, C was a step backward that the entire industry copied)
6. Dependency management that was only a hair above "just `git clone` it into your project" for most of Go's lifetime
An example of such a reference would be this quote by rfiat at https://news.ycombinator.com/item?id=31603329
> It's interesting that you find Go to be Rust-adjacent. Setting aside the USPs of each language (goroutines and borrow checker respectively) and just speaking about the general experience of writing code, I find Go painful for all the reasons that I find Rust pleasant.
> The best summary I can give of Rust is that it was designed by people who learned from the mistakes of the programming languages that came before it. The best summary I can give of Go is to quote Rob Pike's response [0] to a request for syntax highlighting in the Go playground:
>> Syntax highlighting is juvenile. When I was a child, I was taught arithmetic using colored rods (http://en.wikipedia.org/wiki/Cuisenaire_rods). I grew up and today I use monochromatic numerals.
> [0] https://groups.google.com/g/golang-nuts/c/hJHCAaiL0so/m/kG3B...
dgunay's reply in that same thread ended on the summarization "I like Go and I feel very productive in it, but its commitment to simplicity is dogmatic in many ways and it very much is missing milestone advancements in PLs from the past several decades. It could easily have been made in the 90s."
In real world use ridiculously so.
The lack of exceptions, Option type or ability to chain or manipulate errors means every line ends up wrapped in a 90s style "if error" check. Which ends up making code less robust than languages with more modern approaches like Rust, Scala, Swift etc.