Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the most idiomatic way to merge two error types?

I have a type Foo whose methods may "raise" errors of an associated type Foo::Err.

pub trait Foo {
    type Err;
    
    fn foo(&mut self) -> Result<(), Self::Err>;
}

I have another trait Bar with a method intended to process a Foo. Bar may issue errors of its own (specified by an associated type Bar::Err), but it may also encounter errors generated by the Foo it is processing.

I can see two ways to do this, but I don't know which one would be the most idiomatic to Rust.

The first one embeds a result in a result:

pub trait Bar1 {
    type Err;
    
    fn bar<F: Foo>(&mut self, foo: F) -> Result<Result<F, F::Err>, Self::Err>;
}

The second one merges the two error types into a dedicated enum:

pub trait Bar2 {
    type Err;
    
    fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Choice<F::Err, Self::Err>>;
}

The second one looks semantically cleaner, but creates some hurdles for handling the additional enum.

playground

like image 854
Pierre-Antoine Avatar asked Sep 21 '18 07:09

Pierre-Antoine


2 Answers

Typically you don't do a "merge", but instead use nested errors, like this.

enum IntError {
    Overflow,
    Underflow
}

enum StrError {
    TooLong,
    TooShort,
}

enum GenericError {
    Int(IntError),
    Str(StrError),
}

impl From<IntError> for GenericError {
    fn from(e: IntError) -> Self {
        GenericError::Int(e)
    }
}

impl From<StrError> for GenericError {
    fn from(e: StrError) -> Self {
        GenericError::Str(e)
    }
}
like image 129
hellow Avatar answered Oct 20 '22 18:10

hellow


You should use a trait object Error, and you return the first error that you encounter:

pub trait Bar {
    fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Box<dyn Error>>;
}

or implement your trait like this:

impl Bar for MyType {
    type Err = Box<dyn Error>;

    fn bar<F: Foo>(&mut self, foo: F) -> Result<F, Self::Err>;
}

If you really want to have your two errors (but this is strange because one error suffices to make the process not ok), you can use a crate like failure to create an "error trace".

As a general advice, you should not forget to use the traits from std to add more semantic to your code.

like image 41
Boiethios Avatar answered Oct 20 '22 18:10

Boiethios