Listerone
Listerone

Reputation: 1611

What is the mechanism that allows failure::Error to represent all errors?

For example:

extern crate failure;

use std::fs::File;

fn f() -> std::result::Result<(), failure::Error> 
    let _ = File::open("test")?;
    "123".parse::<u32>()?;
    Ok(())
}

What is the technique that allows failure::Error to be able to represent std::io::Error, the parse error, and any other custom error types? What is the minimal implementation to recreate this ability?

Upvotes: 3

Views: 226

Answers (2)

Sven Marnach
Sven Marnach

Reputation: 602055

There are two mechanisms at play here.

The first mechanism is the question mark operator, which returns Err(From::from(e)) when it encounters an Err(e). If the function return type is Result<T, E>, this allows us to return any error type F that E implements From<F> for.

From the documentation of the failure::Error type, we can see that there is a generic From implementation for all types implementing the failure::Fail trait, and there is a generic implementation of Fail for all types implementing std::error::Error (as long as they are also Send + Sync + 'static). In combination, this allows us to return any type implementing either the failure::Fail trait or the std::error::Error trait. All error types in the standard library implement the Error trait, including std::io::Error and std::num::ParseIntError.

This already explains why the code compiles, but it does not explain how the conversions work internally. This is explained by the second mechanism at play – trait objects. The (slightly redacted) definition of the Error type in the failure crate is this:

struct Error {
    imp: ErrorImpl,
}

struct ErrorImpl {
    inner: Box<Inner<dyn Fail>>,
}

struct Inner<F: ?Sized + Fail> {
    backtrace: Backtrace,
    failure: F,
}

The Inner type stores the error as a trait object that uses dynamic dispatch for method calls.

Upvotes: 2

Fuji
Fuji

Reputation: 29834

The failure::Error::from_boxed_compat constructor is used to convert any error to a failure::Error.

pub fn from_boxed_compat(err: Box<dyn StdError + Sync + Send + 'static>) -> Error

This funciton takes any struct that impl Error as input and constructs a failure::Error https://docs.rs/failure/0.1.5/failure/struct.Error.html#impl

failure::Error contains a heap stored trait object inside it that can store a struct that implements the Error object.

struct ErrorImpl {
    inner: Box<Inner<Fail>>,
}

Additionally, it looks like the Fail trait is implemented for many errors. The ? operator will add an into method that will convert an Error to a failure::Error

Upvotes: 1

Related Questions