Best way to deal with multiple error types

fn main() -> Result<T,E> {
 ...
 x = f1()?;
 y = f2()?;
 z = f3()?;
 ...
 Ok(())
}

In this example, suppose f1, f2, f3 return results with different error types.

I know that the above code cannot be correct because all the ? error types have to match that of the main function. What's the best way to deal with this without changing the structure too much?

Usually, there are two ways to deal with this:

  1. 'Erase' the type by using dyn Error:
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let x = f1()?;
        let y = f2()?;
        let z = f3()?;
        Ok(())
    }
    
    This is useful if you only care about the message the errors might return, and not their actual type.
  2. Create an enum that contains the 3 types:
    enum MyError {
        Error1(Error1),
        Error2(Error2),
        Error3(Error3),
    }
    
    impl From<Error1> for MyError { ... }
    impl From<Error2> for MyError { ... }
    impl From<Error3> for MyError { ... }
    
    fn main() -> Result<(), MyError> {
        let x = f1()?;
        let y = f2()?;
        let z = f3()?;
        Ok(())
    }
    
    Since this is a bit tedious, you can use the crate thiserror to make the process easier :slightly_smiling_face:
5 Likes

It's worth to mention, since it isn't obvious, that ? will try to convert the error contained in a Result to the type declared in the containing function's Result using Into, and that's what no2 is.

4 Likes

I'm glad this was addressed in the official documentation. Last paragraph.
https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html

Yes, the box was what I needed.

1 Like