Can I implement Error for a foreign error type?

The functions of backblaze-b2 return a Result with an error type B2Error that doesn't implement the Error trait.

To be able to use these function in other functions that return Result<(), Box<dyn Error>>, I have written this wrapper:

    use backblaze_b2::B2Error;
    use std::fmt::{Display, Formatter};
    use std::error::Error;

    #[derive(Debug)]
    pub struct ErrorWrapper {
        b2_error: B2Error,
    }
    impl Display for ErrorWrapper {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            std::fmt::Display::fmt(&self.b2_error, f)
        }
    }
    impl Error for ErrorWrapper {}

    pub trait IntoError {
        fn into_error(self) -> ErrorWrapper;
    }

    impl IntoError for B2Error {
        fn into_error(self) -> ErrorWrapper {
            ErrorWrapper{
                b2_error: self
            }
        }
    }

Then I call the backblaze-b2 functions like this:

let auth = cred.authorize(&client).map_err(|e| e.into_error() )?;

I would like to get rid of the .map_err(|e| e.into_error() ) if possible.

However, I can't simply implement Error for B2Error because of the orphan rule. Is there another way to get rid of the .map_err(...)?

I don't think you can get rid of all your boilerplate, but you could implement a custom trait for Result<T, B2Error> to turn it into a Result<T, ErrorWrapper>.

A more robust solution is submitting a pull request adding an Error implementation to the backblaze crate.

1 Like

A custom trait that then gets picked up by the question mark operator to do the conversion? I thought it's called Try, but I can't find the docs right now. :thinking:

You can implement impl From<B2Error> for ErrorWrapper this should allow to use the ? on Result<T, B2Error> in functions that return Result<T, ErrorWrapper> but only in those cases. If you return Result<T, Box<dyn Error>> Rust can not decide which into() implementation should be used.

To reduce the boilerplate a little bit you can use a custom extension trait like:

trait ResultExt<T> {
    fn wrap_err(self) -> Result<T, ErrorWrapper>;
}

impl<T> ResultExt<T> for Result<T, B2Error> {
    fn wrap_err(self) -> Result<T, ErrorWrapper> {
        self.map_err(|e| e.into_error())
    }
}

Then you can use it this way:

use path::to::ResultExt;

let auth = cred.authorize(&client).wrap_err()?;
1 Like

Or, in a slightly more generic form that lets you do the same thing for muliple non-Error failure types:

trait ResultExt<T> {
    type Err: Error;
    fn wrap_err(self) -> Result<T, Self::Err>;
}

impl<T> ResultExt for Result<T, B2Error> {
    type Err = ErrorWrapper;
    fn wrap_err(self) -> Result<T, Self::Err> {
        self.map_err(|e| e.into_error())
    }
}
1 Like

@alice That's your library, right? Would you appreciate such a PR at this time, given that you dislike the synchronous nature of the library?

You are welcome to submit PRs to the synchronous version of the library, and I will happily review them, merge them, and publish them. That said, I'm not going to be working on the library for a while.

It is an oversight that it does not implement the Error trait.

Note also that a friend of mine wrote the raze crate, which has the same purpose, except that it has no plans to add an asynchronous api. It may suit your purposes better.

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