Help! Handling Errors and Panic in a command-line program

What is the best way of traping a Panic (such as a divide by zero) in the function where it happens?
I can add a panic hook as shown but don't how to trap in a function like _run(). I'm used to try/catch!!
And, why is "err.message" only available in Rust nightly?

indent preformatted text by 4 spaces
use std::error::Error;
use std::panic;
use std::process::exit;

//How to trap the divide by zero (or any other) within this function?
fn _run() -> Result<(), Box<dyn Error>> {
    println!("In run()");
    let _a = 12;
    let _b = 0; //Intentional divide by zero
    let mut _c = _a / _b;
    println!("Result is: {}", _c);
    println!("Leaving run()");
    Ok(())
}

fn main() -> Result<(), Box<dyn Error>> {
    panic::set_hook(Box::new(|err| {
        if let Some(location) = err.location() {
            println!(
                "Custom Panic: type '{:?}' occurred in file '{}' at line {}",
                err.payload(),
                location.file(),
                location.line()
            );
        //#[cfg(feature = "nightly")]
        //WHY IS THIS IN NIGHTLY ONLY?  Does contain divide by zero message
        //let message = err.message().map(|m| format!("{}", m));
        } else {
            println!("Custom Panic: occurred but can't get location information...");
        }
        exit(1);
    }));

    println!("Hello, world!");
    match _run() {
        Ok(_v) => println!("Good! run() succeeded"),
        Err(_e) => println!("Error in run() is {:?}", _e),
    }
    println!("Bye!");
    Ok(())
}

Use catch_unwind instead.

The idiomatic way to handle errors in Rust is not to catch panics (panics may not even be able to be caught, for example with the panic = abort setting).

Instead, if you anticipate and want to detect division by 0, use checked_div() and optionally propagate the returned Option using the ? operator.

Thank you for your helpful replies.
I have these two solution fragments, either of which stop the program from exiting.
Do please let me know if there are possible improvements.
The function _my_divide() was a solution to avoid the error, "&mut i32 may not be safely transferred across an unwind boundary", but it was not necessary.

////1. Catch Unwind
fn _my_divide(a: i32, b: i32) -> i32 {
    a / b
}
////
let mut _b = 0; //Intentional divide by zero
let mut _c = 1234;
let _c = panic::catch_unwind(|| _my_divide(_a, _b));
//Alternative
//let _c = panic::catch_unwind(|| _a / _b);
let _c = match _c {
    Ok(_v) => {
        println!("Catch Run() Good! {}", _v);
        _v
    }
    Err(_e) => {
        println!("Catch Run() Error is {:?}", _e);
        0
    }
};
println!("Result of catch_unwind is: {}", _c);

////2. Checked_Div
let _a: i32 = 12;
let mut _b: i32 = 0; //Intentional divide by zero
let mut _c: i32 = 1234;
let mut _c = match _b.checked_div(_b) {
Some(_) => {
    println!("Some path: Value of b: {}", _b);
    _a / _b
}
None => {
    println!("None path: Value of b: {}", _b);
    //exit(1);
    0
}
};
println!("Result of checked_div is: {}", _c);
/////

I don't understand why you are repeating the division here. checked_div returns the result of the division inside the Some variant if it succeeds. If you want to just default to 0 if the division fails, you can simply do:

let c = a.checked_div(b).unwrap_or(0);

That's much simpler and doesn't repeat any computation needlessly. Most of the muts in your code are also unnecessary. It's highly advisable to run your code through the Clippy tool and fix the warnings it produces.

1 Like

Thanks for the correct answer! Alas my brain was stuck thinking checked_div() was just "checking" and not "doing" The syntax makes sense now! I hadn't discovered unwap_or(), and that does not panic.
Clippy was selected in Clion but I think not active, but I was just being careless as I tried different things with the code.
Rust is an interesting language: Years ago I was a project manager and was annoyed having to pay expensive C lang contractors to fix the core-dumps they had coded the day before!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.