Minor nit though: Rust doesn't have exceptions.
Minor nit though: Rust doesn't have exceptions.
This makes panics completely unreliable for error reporting.
There has always been a debate what exceptions are for. Python leans heavily towards "exceptions don't have to be exceptional, they can be normal control flow", while Rust is in the other side of the spectrum closer to "if it's something you might want to handle it's not exceptional enough to be an exception". Java is somewhere in between. All of them are valid view points.
A language without reliable exception mechanism is C, where even the most unusual errors are communicated through return codes or global flags that you are expected to check manually
Not really. If it instantly terminates the program without recourse, it's not an exception system: by definition, exceptions come with exception-handling of some sort.
It's just bad form to do so in Rust because it's bad for maintainability to design programs like that.
Rust is opinionated and very much NOT an anything goes language. There is a culture and the culture says do not catch panics. If you feel the need to catch a panic, make it a regular value instead.
The actual difference is that you can set panic=abort as the user of the code (it's a compiler parameter), in which case there is no unwinding and the program aborts immediately.
To me, the lack of possible handling and recovery (based on a compilation flag) is what arguably disqualifies it as an exceptions system.
A bit like saying trains and chairs are functionally the same since they both can have wheels; when there's the crucial difference that chairs don't go choo-choo.
No, these don’t matter. Every langage with exceptions uses them differently, that doesn’t make Python’s no-exceptions and Java’s exceptions.
Regardless of how it’s used, it either is or is not an exceptions system. That’s what “functionally” means.
For instance Go’s panics are an exceptions system, you can always recover() and handle the exception during its unwinding.
> A bit like saying trains and chairs are functionally the same since they both can have wheels
No, it’s like saying an HSR and an ore train are functionally similar, because both are trains even though they’re used differently, in different contexts, and with different performance profiles.
On hardware level exceptions are a synonym for traps.
Java has the checked/unchecked exception divide, where checked exceptions for expected errors that need to be handled specifically, and unchecked exceptions for unexpected errors, matching the use cases of Result and panic respectively in Rust. Unfortunately Java's checked exceptions are cumbersome and don't integrate well with interfaces or generics.
In C# the role of exceptions is not so clearly defined, largely due to the lack of a good alternative error handling mechanism. You have influential developers like Eric Lippert (then part of the C# compiler team) argue against using exceptions for expected errors https://ericlippert.com/2008/09/10/vexing-exceptions . Though I'd go further and would call the use of exceptions to signal IO errors an unfortunate design decision of the language/framework designers. My own experience as web developer is that exceptions will almost always be caught only at the request handler level and turn into an http error.
In C++ the standard library doesn't use exceptions for most errors. And it's common to avoid exceptions entirely (e.g. the google style guide forbids them).
So I'd say the role of unchecked exceptions is not too different from panics in many other languages, though the situation tends to be more messy in Rust due to legacy code and the lack of good alternative error handling mechanisms.
https://doc.rust-lang.org/nomicon/exception-safety.html
Also, there is something similar to try/catch in Rust to catch exceptions, although the docs emphasize that you shouldn’t use for typical error handling. It’s mainly for when Rust is called from the outside (typically a C program). This is because you don’t want a Rust exception to unwind to external code, or all kinds of weird undefined behavior will happen.
The Result<T,E> type is very explicit, and can be easily ignored, composed or "re-thrown" with the "?" suffix without nested try/catch blocks. Return value based error handling is something Go, Elixir and other more functional languages have also adopted.
Panic is there to aleviate the really exceptional circumstances, when the trade-off for possible program termination is worth the much simplified error handling, such as when casting to a smaller integer type in case of overflow, or locking a mutex when it may be in a posioned state (which, funnily enough, can arrive when a panic occurred whilst the mutex was previously locked, i.e. the mutex guard is dropped during a panic).
It would be nice if Rust grew "panic annotations" so that we could determine shallowly and with automated tooling whether functions could panic. It would make it easy to isolate panicky behavior, and in places where it is absolutely necessary to handle, ensure that we do.
> If the function does panic (or the compiler fails to prove that the function cannot panic), the program fails to compile with a linker error that identifies the function name.