#error #main #print #debug #display

main_error

Print errors with Display instead of Debug when using ? in main()

2 releases

0.1.1 Jul 18, 2020
0.1.0 Sep 24, 2019

#155 in Command-line interface

Download history 198/week @ 2021-02-23 279/week @ 2021-03-02 297/week @ 2021-03-09 341/week @ 2021-03-16 275/week @ 2021-03-23 183/week @ 2021-03-30 227/week @ 2021-04-06 156/week @ 2021-04-13 248/week @ 2021-04-20 287/week @ 2021-04-27 260/week @ 2021-05-04 182/week @ 2021-05-11 172/week @ 2021-05-18 281/week @ 2021-05-25 277/week @ 2021-06-01 395/week @ 2021-06-08

1,031 downloads per month
Used in less than 10 crates

MIT license

8KB

main_error build status

Print errors with Display instead of Debug when using ? in main(). For example:

use main_error::MainError;

fn main() -> Result<(), MainError> {
    Err("string or a custom error type")? // prints using Display, not Debug
}

For more info, see:


lib.rs:

Print errors with [Display] instead of [Debug] when using ? in main().

TL;DR

Use like:

use main_error::MainError;

fn main() -> Result<(), MainError> {
    Err("string or a custom error type")? // prints using Display, not Debug
}

See below for more details.

Problem

Since Rust 1.26, main can return a Result<T, E>. This enables the use of ? for convenient error handling (RFC). For example:

# use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
    let num: i32 = "not a number".parse()?; // will fail and print an error
    // ...
#     Ok(())
}

Unfortunately, the error is printed via [Debug] (hardcoded in the standard library), which gives not very pretty or human-friendly output. For example, the error above is printed as:

Error: ParseIntError { kind: InvalidDigit }

Solution

This crate provides [MainError] as a drop-in replacement for the error type E in your main's Result<T, E>. It prints the error via [Display] instead of [Debug], which yields a nicer error message. For example, the program above can be changed to

use main_error::MainError;

fn main() -> Result<(), MainError> {
    let _: i32 = "not a number".parse()?;
    // ...
#    Ok(())
}

and now prints:

Error: invalid digit found in string

Details and Drawbacks

  • [MainError] stores the original error as Box<dyn Error>. This incurs one allocation (on conversion) and one virtual call (on printing). Since there can be exactly one error like this before the program ends, this cost is insignificant.
  • [MainError] implements [From] for all types that can be converted into a Box<dyn Error>.
    1. This allows it to be used in place of any type that implements the [Error] trait (see example above).
    2. It can also be used in place of any type that can be converted to a Box<dyn Error>, e.g., String.
  • [MainError] does not implement the [Error] trait itself.
    1. It doesn't have to, because the standard library only requires E: Debug for main() -> Result<T, E>.
    2. It doesn't need to, because the [Error] trait is mostly for interoperability between libraries, whereas [MainError] should only be used in main.
    3. It simply cannot, because this would create an overlapping impl. [MainError] can be converted from Into<Box<dyn Error>>. Into<Box<dyn Error>> is implemented for E: Error itself. If [MainError] impl's Error, it would mean [MainError] could be converted from itself. This collides with the reflexive impl<T> From<T> for T in core.
  • [MainError] implements [Debug] in terms of [Display] of the underlying error. This is hacky, but unfortunately [Debug] as the output for the main error case is stable now. The "Error: " part at the beginning of the output comes from the standard library, thus it cannot be changed.

No runtime deps