What are the differences between panic in Rust and exception in C++?

Could somebody please explain them to me? I seem to have vague idea about the differences but if put at the spot I wouldn't be able to give comprehensive answer.
Thank you

I haven't worked with C++ exceptions a lot, but I know exceptions in general. I think the difference between exceptions in general and Rust's panics might be:

  • It's not guaranteed that a panic can be caught (you can demand an abort on panic during compilation).
  • Thus panics should not be used for recoverable errors in general.
  • When catching a panic, you get a dynamically typed object implementing the Any trait, see std::thread::Result, which is returned by std::panic::catch_unwind. This may be troublesome to make use of.

I think catching panics is mostly for making a multithreaded process not abort but keep functioning in case of serious, non-recoverable errors.

Even though Lua and Rust are fundamentally different languages, I feel like Lua's errors (raised with the error function) are similar to Rust's panics. Respectively, returning a nil as first value and an error_value as second return value in Lua corresponds to returning std::result::Result::Err(error_value) in Rust.

Lua, like Rust, doesn't have exceptions.

1 Like

Hi and thanks for the answer.
It is the same with exceptions. They are not guaranteed to be caught and in such situation, program aborts.

What I meant is that in Rust it's not guaranteed that a panic will be caught, even if you use catch_unwind.

When you have a try block in C++ with a catch that matches the exception, then it will always catch the exception, right?

I believe yes, if the correct type of exception is being specified in the try catch block.

So here is the difference: If you use catch_unwind in Rust, then it's not assured that the whole program won't abort in case of panic.

Hehe, nice. But seams as a somehow not really solid difference, if you know what I mean.

C++ exceptions are generally an instance of some class, whereas Rust panics are generally just a string.

1 Like

I think it is a relevant difference. It means, for example, if I construct an API which opens a remote database, and I report network errors by panicking, it would be impossible to use the API without risking to abort the program if a network error happens.

Sure, it is.
[Edit]
After seeing @alice reply, it seems to me that this isn't really the case.

Rust panics can reliably be caught. As long as you don't configure your binary to abort on panic, then you are guaranteed to catch them when using catch_unwind.

But a library will have no influence on what a binary depending on that library might do.

1 Like

I think the main difference is the intent behind them. The intended use of a Rust panic is that you should only use them if your program hits a bug. This is why panics are just strings, rather than being some object with information about the failure that you might use to recover from the panic.

On the other hand, C++ exceptions are intended to be usable for errors that are not bugs.

14 Likes

Not sure what "generally" means in this context, but Rust panics aren't required to be a string:

fn main() {
    let x = std::panic::catch_unwind(|| {
        std::panic::panic_any(15);
    }).unwrap_err();
    let y = *x.downcast::<i32>().unwrap();
    assert_eq!(y, 15);
    println!("Control flow reaches end of main (and y={y}).");
}

(Playground)

Output:

Control flow reaches end of main (and y=15).

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 0.69s
     Running `target/debug/playground`
thread 'main' panicked at 'Box<dyn Any>', src/main.rs:3:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

This also is similar to Lua:

Errors in Lua (like panics in Rust) are usually a string, but can have any (i.e. Any) type.

1 Like

I think one exception (no pun intended) might be that certain non-recoverable errors may cause panics too, even if they are not bugs. To name one example: Memory exhaustion (maybe it's the only example :sweat_smile:).

By "generally", I mean that nobody ever uses anything other than a string, even if you technically can.

3 Likes

Thanks, that nails it!

Note that this property may not hold in the future if RFC 3288 is implemented, which would make panics in Drop always abort, with no stable workaround. I'm against that change myself (on account of this property, as well as the other reasons I wrote there), but I'm hardly the one running the project.

4 Likes

It already doesn't hold, [1] panics while panicking abort. And that's the plan for non-unwind FFI functions too.


  1. even when you control the panic strategy ↩︎

4 Likes

This.

Rust panic is implemented using the same underlying mechanisms as C++ throw. So the important differences are on the human side of things.

1 Like