Use error trait in generic impl

Hi,

Again, I come with a new problem.

In fact, I am trying to implement a generic with error trait.
Dealing with errors looks complex in Rust, or I maybe not understand something ?

So here the code and the error :

Code
pub trait OutputMessage {}

pub trait OutputBoundary<T, V>
    where
        T: OutputMessage,
        V: Error,
{
    fn success(&mut self, message: T);
    fn error(&mut self, message: T, error: V);
}

pub struct ElementPresenter {
    view_models: Option<Vec<ElementViewModel>>,
    error: Option<Box<dyn Error>>,
}

impl ElementPresenter {
    pub fn new() -> Self {
        Self {
            view_models: None,
            error: None,
        }
    }
}

impl<E: Error> OutputBoundary<AddElementOutputMessage, E> for ElementPresenter {
    fn success(&mut self, message: AddElementOutputMessage) {
        if let Some(e) = message.get_element_entity() {
            self.view_models = Some(vec![ElementViewModel::from(e)]);
        }
    }

    fn error(&mut self, message: AddElementOutputMessage, error: E) {
        if let Some(e) = message.get_element_entity() {
            self.view_models = Some(vec![ElementViewModel::from(e)]);
        }

        self.error = Some(Box::new(error));
    }
}
Error
error[E0310]: the parameter type `E` may not live long enough
  --> application\src\presenters\element_presenter.rs:32:27
   |
20 | impl<E: Error> OutputBoundary<AddElementOutputMessage, E> for ElementPresenter {
   |      -- help: consider adding an explicit lifetime bound...: `E: 'static +`
...
32 |         self.error = Some(Box::new(error));
   |                           ^^^^^^^^^^^^^^^ ...so that the type `E` will meet its required lifetime bounds

Other question : I have seen, while parsing the internet, some topic on crate chain_error.
Is it a must have ? Because next I plan to have errors with list of cause.

When you write this:

pub struct ElementPresenter {
    // ...
    error: Option<Box<dyn Error>>,
}

the compiler interprets it as though you'd written this:

pub struct ElementPresenter {
    // ...
    error: Option<Box<dyn Error + 'static>>,
}

Box<dyn Error + 'static> means "a Box containing a value of some type that implements Error, and that can't contain any non-'static references" (the second part is what + 'static refers to). But in your generic impl block, which starts with impl<E: Error>, the type parameter E can be any type that implements Error, including types that can contain non-'static references:

#[derive(Debug)]
struct HasNonStatic<'a>(&'a str);
impl<'a> Display for HasNonStatic<'a> { /* ... */ }
impl<'a> Error for HasNonStatic<'a> {}

Then in the implementation of error, you try to use a Box<E> where a Box<dyn Error + 'static> is expected. Coercing between these types is only allowed if E: Error + 'static, and since the impl block only guarantees E: Error, it's not permitted here.

As the error message suggests, you can fix this by changing impl<E: Error> to impl<E: Error + 'static>.

It's definitely not a must-have—you can always roll your own error types and implement the necessary traits (Display, Error) yourself, and error_chain (which is the crate I think you're referring to) is not even the most widely-recommended crate for such things these days; that would probably be thiserror.

3 Likes

Thanks.

Oh yeah, I missread the error... :sweat_smile:
I have tried to add 'static in the Box (while I already know that it allowed to have a size known object).

If I have understood correctly, a non-static object is an object wich have a specific lifetime parameter in it definition ?

So is there a way to represent an error type wich can be non static in my impl ?
Because actually, I do not really know wich type of errors I will implement.
I have tried to implement it, but it do not match OutputBoundary specification... :thinking:

Function implement try
fn error<'a>(&mut self, message: AddElementOutputMessage, error: Box<dyn 'a + Error>)

Thanks for the link. I will take a look at it next launch :slight_smile:

Sort of. I would think about it as: a type T is 'static if values of that type can never contain a non-'static reference (there's also PhantomData to think about but that's less important). So:

  • &'a str is not 'static unless 'a = 'static
  • u8 is 'static
  • String is 'static
  • Vec<u8> is 'static
  • Vec<&'a mut [u8]> is not 'static uness 'a = 'static
  • Box<[u8]> is 'static
  • Box<dyn std::error::Error + 'static> is 'static
  • a struct whose fields are all 'static is 'static
  • an enum whose fields (across all variants) are all 'static is 'static

It's true that because 'static is the only named lifetime, a non-'static type has to spring from some kind of generic type, like Foo<'a> (lifetime generic parameter) or Bar<&'a str> (type generic parameter).

You should make all your error types 'static if possible, and not worry too much about being generic over non-'static error types. Non-'static types are a pretty specialized tool; using them to represent errors is not common or encouraged, and it will make your life harder.

That said, here's how you'd generalize your code so that ElementPresenter can hold non-'static error data:

pub struct ElementPresenter<'err> {
    // ...
    error: Option<Box<dyn Error + 'err>>,
}

impl<'err, E: Error + 'err> OutputBoundary<AddElementOutputMessage, E> for ElementPresenter<'err> {
    // ...

    fn error(&mut self, message: AddElementOutputMessage, error: E) {
        // the same code as before
    }
}

Thanks a lot for yours explanations, and the time you spend to write it here :slight_smile:
I think I have understand this now !

Last question :
I do not remember ever having met the 'err.
What does it represent ?

1 Like

'err is a descriptive name for the generic lifetime in the struct declaration and the impl block. (Lifetime generic parameters can be named anything you want, just like type generic parameters.) I could have used struct ElementPresenter<'a> and impl<'a, E: Error + 'a>, or struct ElementPresenter<'a> and impl<'b, E: Error + 'b>, and it wouldn't make any difference to the compiler—the name 'err is just a little reminder to the human reader that "this lifetime parameter is related to the error field of ElementPresenter".

1 Like

Oh ok, I feel stupid now ! :sweat_smile:
Damn, my brain is out... I know that we can name it as we want ! x)

Thanks again :slight_smile: