[][src]Crate main_error

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

TL;DR

Use like:

This example panics
use main_error::MainError;

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

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:

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

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

This example panics
use main_error::MainError;

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

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.

Structs

MainError

Newtype wrapper around a boxed std::error::Error.