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?

3 Likes

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:
7 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.

5 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.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.