So I am trying to understand how ?
propagate errors and also interaction with Box<dyn Error>
So after reading the part about Error from the Rust book, I come away with the following understanding:
- The
?
when used on aResult
expression will cause early return in case theResult
is anErr
variant - In the case when the error type of the enclosing function (say
ReturnedError
) is different from the error type?
is applied on, (sayOriginalError
), there has to be a conversion. The conversion works if there is an implementation ofFrom<OriginalError> for ReturnedError
. - The
main
function's return type can beResult<(), Box<dyn std::error::Error>>
this allows for possibility to return anystd::error::Error
So to put this in practice I came up with this code
fn main() -> Result<(), Box<dyn std::error::Error>> {
Err(CustomError::GenericError("hahaha".to_string()))?;
Ok(())
}
This fails to compile with following error
error[E0277]: the trait bound `CustomError: StdError` is not satisfied
--> src/main.rs:86:57
|
86 | Err(CustomError::GenericError("hahaha".to_string()))?;
| ^ the trait `StdError` is not implemented for `CustomError`
|
= help: the following other types implement trait `FromResidual<R>`:
<Result<T, F> as FromResidual<Result<Infallible, E>>>
<Result<T, F> as FromResidual<Yeet<E>>>
= note: required for `Box<dyn StdError>` to implement `From<CustomError>`
= note: required for `Result<(), Box<dyn StdError>>` to implement `FromResidual<Result<Infallible, CustomError>>`
I am not sure what StdError
is and why it has to be implemented for CustomError
.
I decided to go with what I have read and assumed that for this to work, I need an implementation of the From trait that will help convert from CustomError
to Box<dyn std::error::Error>>
. So I added this:
impl From<CustomError> for Box<dyn std::error::Error> {
fn from(e: CustomError) -> Self {
Box::new(e)
}
}
This also fails to compile with the following error
error[E0277]: the trait bound `CustomError: StdError` is not satisfied
--> src/main.rs:77:9
|
77 | Box::new(e)
| ^^^^^^^^^^^ the trait `StdError` is not implemented for `CustomError`
|
= note: required for the cast from `CustomError` to the object type `dyn StdError`
After following the prompts of my IDE, and googling I finally came up with a version that compiles, which is:
#[derive(Debug)]
enum CustomError {
GenericError(String),
SpecificError(String),
}
impl std::fmt::Display for CustomError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CustomError::GenericError(s) => write!(f, "{}", s),
CustomError::SpecificError(s) => write!(f, "{}", s),
}
}
}
impl std::error::Error for CustomError {
fn description(&self) -> &str {
match self {
CustomError::GenericError(s) => s,
CustomError::SpecificError(s) => s,
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
Err(CustomError::GenericError("hahaha".to_string()))?;
Ok(())
}
But I find this even more confusing because there is no implementation of StdError
in the version that compiles and yet StdError
was showing up in the error messages.
So my question are,
- what exactly is
StdError
- How does it interact with
?
because it seems using?
requires it somehow - Why did the version that compiled did so?
Update
So came up with this version that also works
impl Display for CustomError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CustomError::GenericError(s) => write!(f, "{}", s),
CustomError::SpecificError(s) => write!(f, "{}", s),
}
}
}
impl StdError for CustomError {
fn description(&self) -> &str {
match self {
CustomError::GenericError(s) => s,
CustomError::SpecificError(s) => s,
}
}
}
seems StdError
is some sort of alias for std::error::Error
?