Reputation: 1611
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
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
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