Recover in Rust

In Rust, panic handling is done through the Result type and the ? operator for error propagation. However, we can simulate a similar behavior to Go’s recover using std::panic::catch_unwind.

use std::panic;

// This function panics.
fn may_panic() {
    panic!("a problem");
}

fn main() {
    // We use catch_unwind to capture a panic
    let result = panic::catch_unwind(|| {
        may_panic();
    });

    // Check if there was a panic
    match result {
        Ok(_) => println!("Execution completed without panic"),
        Err(e) => {
            if let Some(s) = e.downcast_ref::<&str>() {
                println!("Recovered. Error:\n {}", s);
            } else {
                println!("Recovered from panic with unknown error");
            }
        }
    }

    // This code will run, because catch_unwind caught the panic
    println!("After may_panic()");
}

In Rust, we use std::panic::catch_unwind to catch panics. This is somewhat similar to Go’s recover, but with some important differences:

  1. catch_unwind is used to catch panics in a specific scope, rather than using a deferred function.
  2. It returns a Result, which we can then match on to handle the panic.
  3. Not all panics can be caught by catch_unwind, particularly if the panic was caused by stack overflow or if the panic payload isn’t safely unwindable.

The downcast_ref method is used to try to extract the panic message as a string slice. If successful, we print it; otherwise, we print a generic message.

When you run this program, you’ll see:

$ cargo run
Recovered. Error:
 a problem
After may_panic()

This demonstrates that we’ve successfully caught the panic, printed the error message, and continued execution.

It’s important to note that while this pattern can be useful in some scenarios (like in FFI code or for some high-level error handling), it’s generally not recommended to use catch_unwind for normal error handling in Rust. Instead, Rust encourages using the Result type for expected error conditions.