7

In development, my code uses .expect() and panic!() to handle fatal errors.

Their behaviour is exactly what I need during development.

When I compile with --release, I was hoping that their output would become more succinct.

i.e. This code:

let mut file_a = OpenOptions::new().write(true)
                                   .read(true)
                                   .open(args.file_a).expect("foo bar");

generates:

thread 'main' panicked at 'foo bar: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/bin/vsapply.rs:131:59

When I would prefer just:

foo bar: No such file or directory

when compiled with --release.

Is there already a way of doing this?

fadedbee
  • 42,671
  • 44
  • 178
  • 308

2 Answers2

7

One option you have available to you is, rather than flatly panicking, instead printing information to stderr by using eprintln!() and exiting with a non-zero error code.

use std::process;

fn main() -> {
    // some terrible state
    eprintln!("foo bar: No such file or directory.");
    process::exit(1);
}

This has a couple of benefits:

  • Printing to stderr ensures that your error message isn't instead directed to some output file (e.g. foo --option arg1 arg2 > output.txt).
  • A program calling your program can interpret a non-zero error code as an indication of failure.
ebwb
  • 264
  • 5
  • 20
  • Thanks for your answer. Every second or third line of my code is an `unwrap()` or `expect()`. Checking each condition manually would be too verbose and make the code unreadable. Your point about stderr is important - I'll use the answer from @akihito-kirisaki with `eprintln!()`. – fadedbee Dec 13 '20 at 06:36
  • This follow https://clig.dev/ command line guidelines and I like it – Cirelli94 May 13 '22 at 13:40
4

Combination of std::panic::set_hook and #[cfg(debug_assertions)] soleves the problem.

use std::panic;

fn main() {
    #[cfg(not(debug_assertions))]
    panic::set_hook(Box::new(|panic_info| {
        if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
            println!("panic occurred: {:?}", s);
        } else {
            println!("panic occurred");
        }
    }));
    println!("Hello, world!");
    panic!("nyaan");
}

When exec cargo run, output is:

Hello, world!
thread 'main' panicked at 'nyaan', src\main.rs:14:5

When cargo run --release:

Hello, world!
panic occurred: "nyaan"
Akihito KIRISAKI
  • 1,243
  • 6
  • 12
  • 1
    This doesn't quite work. With `--release` the hook code is called, but the extra information is not printed. e.g. without `--release` I get `thread 'main' panicked at 'This delta expects file_b to be 43999 bytes long, not 44020 bytes.', src/bin/vsapply.rs:109:9 note: run with 'RUST_BACKTRACE=1' environment variable to display a backtrace` but with `--release` I just get `panic occurred`. (The calling code is `panic!("This delta expects file_b to be {:?} bytes long, not {:?} bytes.", len, blen);`.) – fadedbee Dec 13 '20 at 06:45
  • It seems good to keep default `panic!` with `take_hook` and call it in custom `panic!`. https://www.ncaq.net/2019/07/11/18/18/12/ – Akihito KIRISAKI Dec 13 '20 at 07:00