Actually, the default panic handler prints the stack trace (if enabled) regardless of whether the unwind is caught or not.
C# crashes much more than Rust; it crashes on any uncaught exception.
Unless you put a blanket catch at the top-most function, which I suspect is considered a bad practice (it definitely is so in Java).
And if you try to catch it in C#, you'll find out that it doesn't actually work most of the time:
C# crashes much more than Rust; it crashes on any uncaught exception.
That is daft. That never happens in practice, one catches exceptions where one can recover and continue or abort cleanly with useful a error message, and always inside main()
.
Unless you put a blanket catch at the top-most function, which I suspect is considered a bad practice (it definitely is so in Java).
main()
for those who know C#. If the exception can only be handled as a fatal error then catching it there is fine.
This is equivalent to in Rust "we'd never use unwrap
of course".
You should come here with an open mind and willing to learn new ways to think.
Idiomatic Rust does't catch panics, it makes use of sum types. Just like C# doesn't have sum types, it has exceptions. That, and different semantics, makes for different APIs. The other big difference is that Rust has little to no runtime, while C# is probably one of the state-of-the art managed languages.
C# does not crash much more than Rust. Having a try{}catch in main()
is basic and means there are never any uncaught exceptions.
I do appreciate your guys’ insight into Rust, but misleading comments on C# are not.
The question is, does this really have any semantic difference from crash (unless you put this try {} catch
in a "retry" loop, of course)? In both cases, the error which is not handled properly bubbles to the program entrypoint and forces it to end - the only thing that's added by simple try {} catch
, as far as I can see, is the human-readable description of error.
Exceptions are usually handled in a loop, say for each incoming message to be processed for a transaction processing app or inside event handlers such as mouse click in GUI apps; both so the app always continues on a failure, never crashing.
Then you continue with an unknown applicaiton state. You may have corrupted data. No thanks, I prefer a crash. If needed, I'll restart the program.
There are techniques to restore data integrity in the exception handler. Losing a ɢᴜɪ user’s changes or many concurrently handled messages is unacceptable in 2022.
Writing a program with logic bugs that cause such crashes (such as an unbounded recursion) has been unacceptable since - I dunno, forever I'd say.
If you're so concerned about the safety of your user's data from an app crash that is caused by faulty logic, then you had better learn to not make such logic errors in the first place.
Achieving that in Rust is quite easy if you help yourself with the type system and the safe APIs (get
vs Index
here).
No, I was referring to any exception, not just logic bugs. FYI unbounded recursion is an abort in Rust and in C# so unrecoverable.
No, exception handling handles logic and runtime errors. The user action rolls back or the message being processed fails.
Are you a doctor?
Yes, the stack gets unwound using the same mechanism as C++ exceptions. This calls destructors of stack values that are unwound past.
There are works, such as the no-panic crate that cheks that specific functions can't panic. There is also something similar for an entire program (that basically checks that there is no panic handler linked into the final binary).
At the same time, any program can crash and any operating system can crash or hang. That's why server programs are run from systems or scripts that restarts them if they terminate, and why server OS:es and embedded systems use hardware watchdogs that reboot the machine if the OS has not declared it's still alive recently enough.
Now, in practice, for a web server in rust, most rust web frameworks uses catch_unvind to catch any panic in a handler (most even serves a proper http 500 response for it). I'm running three different web application serverns that I've written in rust on my home server, (the youngest since january and the other two since four years ago) and have never had a software crash in any of them (I've had several power and network failures though).
Well, if you handle all exceptional cases then your program won't crash unless you have hardware/OS level failures. This is same in Rust and in C#. You can argue that it is easier to do it in C# (just a catch at main) than in Rust (?
at every level, match in main
).
If you have logic errors, you program is wrong. It doesn't matter whether C# keeps your program alive with try/catch and Rust allows the OS to kill it. It is a wrong program, it is faulty software. Fix that instead of blaming the language for not giving you a safety net.
I wish. But no, I am just someone who can write programs that don't contain logic bugs.
The conflation of logic and runtime errors is exactly what exceptions cause and what Rust tries to avoid by having two entirely separate error management mechanisms. If a program has a logic bug, it can't trust its own state and should not try to make forward progress – it should try to best-effort rollback whatever it's doing and unwind to some abstraction boundary – which is exactly what panicking does by default! Rollbacks should be handled by destructors rather than catching panics.
BTW, async fn
moves almost all of its state from the stack to the Future
struct, so if you have a stack-heavy problem to work with, artificially forcing it to use async will let you heap-allocate its "stack".