3 ms·
Even Rust has panic for things like out-of-bounds access. In fact, Rust's panic / Result is actually very similar technically to Java's checked / unchecked exce
by moring 4y ago
Even Rust has panic for things like out-of-bounds access. In fact, Rust's panic / Result is actually very similar technically to Java's checked / unchecked exceptions. I'd argue that this is more a difference in usage and culture, with likely influence from the fact that Java makes it easy to catch unchecked exceptions, so many prefer unchecked exceptions to avoid being forced to think about what to do in case of an error (which, of course, means that probably the wrong thing happens then).
Also, Rust culture (IMHO) tends to be more strict about technical errors, probably because it is applied to more low-level problem areas. If high-level business logic is cluttered with technical error handling in every other line, it becomes near impossible to read and maintain, so other patterns are applied (e.g. atomic transactions that are undone when something fails).
- masklinn 4y ago> I'd argue that this is more a difference in usage and culture Usage and culture and affordances, as in: - the stdlib doesn’t signal errors by panic-ing - rust learning documents drive users towards Result and Option - the standard library provides APIs to manipulate results in ways Java is utterly missing e.g. map_err, ok, ok_or, ?, … - because results are reified values, the language allows abstracting over results (creating macros or functions around them), as well as providing utilities around them (e.g. eyre, thiserror, anyhow, …) - because results are reified values, they can be ignored until relevant, an `Iterator<Item = Result<…>>` is just an `Iterator<Item = _>` until you actually need to process the result. - panics have very little signaling power, technically your panic can carry a payload but I’ve never seen anyone go beyond a message Also rust panics are explicitly noted to not necessarily be recoverable from, so they can not be used for error signaling: whoever compiles the program can set panic=abort, and now you have no error handling at all, the program just straight dies. And even if you actually, genuinely, want to use checked exceptions in Java, it’s hell, because it does not mesh with the rest of the language. Just look at the contorsions necessary to get checked exceptions through Stream APIs. Checked exceptions are not necessarily bad in and of themselves, but Java’s implementation is so awful it has completely poisoned the field. And frankly it just… doesn’t seem like a very useful thing to put work into: you can just use exceptions-flavoured results à la Swift or Zig if you like the “look” of exceptions.
- girvo 4y agoNim does “checked exceptions”-ish via its effects system and {.raises: […].} pragma. Though it’s not just limited to exceptions. Can be used for all sorts of useful things!
- codeflo 4y agoTo add to that, Java doesn’t make it convenient to have a place that catches checked exceptions only, because the inheritance hierarchy is the wrong way around for that. Also, the type system isn’t strong enough in most cases to express something being generic over the exception type, so lots of interface implementations end up having to wrap every exception in a RuntimeException anyway.
- grogers 4y agoIf you are looking to catch only checked exceptions of unknown type, it's easy enough to first catch and rethrow Error and RuntimeException, and then catch Exception and do whatever. The hierarchy is garbage, but at least it's fixed in stone. Not super useful though if you know the exception types.
- peterkelly 4y agomasklinn already made lots of good points. I'd add that panics in Rust are for intended for non-recoverable errors (like memory allocation failures, out-of-bounds access) and under most use cases are equivalent to abort(). There are mechanisms to catch panics in Rust, but the docs state: "It is not recommended to use this function for a general try/catch mechanism. The Result type is more appropriate to use for functions that can fail on a regular basis. Additionally, this function is not guaranteed to catch all panics, see the “Notes” section below." > atomic transactions that are undone when something fails This is exactly what many business apps do frequently, through the use of database transactions. [1] https://doc.rust-lang.org/std/panic/fn.catch_unwind.html https://doc.rust-lang.org/std/panic/fn.catch_unwind.html
- pjmlp 4y agoOne should also note that while Java famously takes the blame, checked exceptions appeared in CLU, were adopted by Modula-2+, Modula-3 and C++, before landing into Java's design. And while C++ has moved away from its original design, it kept the variant of "no exceptions" or "expect any kind of exceptions", and still there are ongoing discussions of eventually getting something similar to Swift's model.
- derefr 4y agoTo me, the key problem with Java’s error handling is that there’s no special consideration given to functional interfaces like Callable/Runnable/etc — so you effectively cannot throw a checked exception from within a lexical closure passed to a Stream.map, ExecutorService.submit, etc: trying to do so gets you a compile error. This was worked around in the design of CompleteableFutures, where the closure must cyclically capture its own future in order to be able to call .completeExceptionally on it; but that requires that every use of functional code essentially subvert Java by having a try-catch at the root of the closure’s AST that turns propagating exceptions, into, essentially, results, via a side-channel; and then the caller of CompleteableFuture.get gets the side-channel exception value thrown back at them and propagating again. Honestly a mess—and people think Golang’s little “if err != nil” checks are bad! (What should actually be happening is “exception generic auto-deduction”: anything that accepts Callable or other functional interfaces should actually accept a (currently-impossible) type “Callable throws <T>…”; and then the list of checked error types of the instantiated object should be deduced from what the called code actually does—as Java perfectly-well knows how to do, in order to assert compiler errors about checked exceptions in the first place. This would also require you to be able to define methods that “pass along” checked exceptions without knowing what they are, though — and without just declaring you throw Throwable and thus discarding the exception typing of the callee — in order for the framework code that takes these Callables to be valid.)