Search code examples
rusterror-handlingthiserror

Convert Result<_, error> to Result<_, MyError> with thiserror in Rust


I'd like to implement a custom enum to wrap multiple errors like the code below.

I know I can make it work with map_err but it does not seem to be neat. And since Result is not defined in the current crate, I can't implement its conversion.

How will you deal with it in this situation?

Looking at wrapped_fn, it seems to implement Result<_, io::Error> conversion. Would you kindly tell me why this can work?

use anyhow::Result;
use std::io;
use thiserror::Error;

#[derive(Debug, Error)]
enum MyError {
    #[error("CustomError {0}")]
    CustomError(#[from] io::Error),
}

struct MyStruct {}

fn error_fn() -> Result<(), io::Error> {
    return Err(io::Error::new(io::ErrorKind::Other, "other"));
}

fn wrapped_fn() -> Result<(), MyError> {
    error_fn()?;  // no idea why this works because it seems like implement `Result` conversion.
    Ok(())
}


fn main() {
    let result: Result<(), MyError> = error_fn().into(); // I want to make this work.
    // let result: Result<(), MyError> = error_fn().map_err(MyError::from); // This works.
}

// This cannot be implemented as well.
// impl<T> std::convert::From<Result<T, io::Error>> for Result<T, MyError>{
//     fn from(result: io::Error) -> Self{
//         result.map_err(MyError::from)
//     }
// }

Error output

error[E0277]: the trait bound `Result<(), MyError>: From<Result<(), std::io::Error>>` is not satisfied
  --> src/main.rs:19:50
   |
19 |     let result: Result<(), MyError> = error_fn().into();
   |                                                  ^^^^ the trait `From<Result<(), std::io::Error>>` is not implemented for `Result<(), MyError>`
   |
   = note: required for `Result<(), std::io::Error>` to implement `Into<Result<(), MyError>>`

Solution

  • The wrapped_fn is what you typically want to do! The ? is actually converting From the existing error to the wanted error type before returning it if there is an error.

    If you really want to do a conversion like the one in your main without returning the error, then I think the nice way to write it is indeed:

    fn main() {
        let result = error_fn().map_err(MyError::from);
    }