I know all about Result, it's great for errors that are recoverable. But there's a lot of errors that aren't really recoverable inside a library, but you may not want to cause the code using that library to crash - perhaps your buggy component can be worked around.
Ocaml has both algebraic data types and exceptions, and works really well.
> But there's a lot of errors that aren't really recoverable inside a library
Aren't all propagated `Result`s examples of errors that the library itself doesn't recover from and lets the caller decide what to do? IMO, panics in libraries are for cases that are either "impossible" (`panic` as `assert`) or the library author made a judgement call that the application is also helpless in this situation (`panic` as `exit`, like OOM in most of `std`).
> you may not want to cause the code using that library to crash - perhaps your buggy component can be worked around.
That's a question of isolation! Rust libraries operate within the same process and address space as the application code. A buggy library could arbitrarily modify the application data or the process state and violate arbitrary application invariants. I'm not even talking about UB here. Just application-level invariants and assumptions.
Given this, the Rust's choice to either "abort the thread and poison held mutexes" or "abort the process" sounds very reasonable.
In other discussions, I was pointed to Erlang's "let it fail" error handling with multiple isolated processes. [The Error Model](https://joeduffyblog.com/2016/02/07/the-error-model/), which I reference in my post, also touches on this topic.
Panics in rust threads do not crash the process. Only panics in the main thread crash the process. So if you run nothing in the main thread, you can take advantage of this.
> Panics in rust threads do not crash the process.
They do on several occasions, as I mentioned in the post:
> the process still crashes if the target doesn’t support unwinding or the project is built with panic = "abort" setting.
The linked guide to error handling in Rust [1] mentions another such case:
> Even in the default unwind-on-panic configuration, causing a panic while the thread is already panicking will cause the whole program to abort. You must therefore be very careful that destructors cannot panic under any circumstance. You can check if the current thread is panicking by using the std::thread::panicking function.
Rust doesn't support that, but there's an RFC trying to figure out how that could be done (hasn't gone anywhere after more than 10 years of discussions): https://github.com/rust-lang/rfcs/issues/294
But Rust supports macros, just like Lisp, so of course someone wrote a library that provides something similar:
Yeah, but there's always a cultural struggle to prevent everyone from using their own incomprehensible DSL. See "The Lisp Curse" [1]. Even vanilla Haskell (without macros, i.e. without Template Haskell) takes it too far by allowing to define arbitrary custom operators with arbirtary associativity and precedence. Many Haskell libraries are an incomprehensible soup of symbols instead of regular functions with readable names. Rust does pretty well so far. Macros are used appropriately in the ecosystem.
The Lisp Curse was written by someone who never worked on a Lisp project, let alone with other people.
It's a good example of what hallucination looked like before AI took it over.
Macros are super helpful in communicating ideas to future maintainers. The alternative to writing a macro and using it seventeen times is to just open code some similar logic in seventeen places.
The macro calls can all be found by name, and if something is wrong, it can be fixed in one place: the macro definition.
Macros run in your development system, and so do not have to be debugged in the live target. Most macris are functional and have only one input: the unexpanded macro call. They can be debugged simply by inspecting the output, and iterating on it.
I know all about Result, it's great for errors that are recoverable. But there's a lot of errors that aren't really recoverable inside a library, but you may not want to cause the code using that library to crash - perhaps your buggy component can be worked around.
Ocaml has both algebraic data types and exceptions, and works really well.