Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement Error::cause properly?

I have a problem implementing the Error trait. I want to wrap an error from Diesel or another database driver. I didn't even get close to implementing From since I'm already failing at implementing Error. The line that causes the code not to compile is the one at the very end of the code block.

use std::fmt;
use std::error::{self, Error};

#[derive(Debug)]
pub enum MyError {
    NotFound(String),
    PersistenceError(Box<Error + Send + Sync>),
}

pub type MyResult<T> = Result<T, MyError>;

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MyError::NotFound(ref msg) => write!(f, "Not found: {}", msg),
            MyError::PersistenceError(ref cause) => write!(f, "Persistence error: {}", cause),
        }
    }
}

impl Error for MyError {
    fn description(&self) -> &str {
        match *self {
            MyError::NotFound(ref msg) => msg,
            MyError::PersistenceError(ref cause) => cause.description(),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            MyError::NotFound(_) => None,
            // `*cause` does not live long enough
            MyError::PersistenceError(cause) => Some(&*cause),
        }
    }
}

I also tried:

*cause does not live long enough

MyError::PersistenceError(cause) => Some(&*cause),

the trait core::marker::Sized is not implemented for the type std::error::Error + Send + Sync + 'static [E0277]

MyError::PersistenceError(ref cause) => Some(cause),

the trait std::error::Error is not implemented for the type `&Box

MyError::PersistenceError(ref cause) => Some(&cause)

But none of these worked.

like image 237
chridou Avatar asked Mar 23 '16 17:03

chridou


1 Answers

It is useful to print the type of variables in cases like this:

match *self {
    MyError::NotFound(_) => None,
    MyError::PersistenceError(ref cause) => {
        let () = cause;
    },
}

This will tell you that cause is a &Box<std::error::Error + Send + Sync>.

If we dereference it once, we will have a Box<std::error::Error + Send + Sync> and if we dereference it a second time we will have a std::error::Error + Send + Sync (this isn't a real type). We can then take another reference which can be implicitly made into an &Error:

match *self {
    MyError::NotFound(_) => None,
    MyError::PersistenceError(ref cause) => Some(&**cause),
}
like image 179
Shepmaster Avatar answered Oct 12 '22 04:10

Shepmaster