Implement `thiserror` `#[from]` on a `PoinsonError`

Hi !

Anyone know how to implement thiserror #[from] on a PoinsonError ?

I have my error type WindowError with a variant which should be build from a PoisonError<GlobalState> (to use ? on the .read() method of Arc<RwLock<GlobalState>>) :

#[derive(Error, Debug)]
pub enum WindowError {
    #[error("State access error")]
    StateAccessError(#[from] PoisonError<GlobalState>),
}

But it seems I don't do it correctly :

error[E0277]: `?` couldn't convert the error to `WindowError`
 --> gui/src/window/part/header.rs:9:34
  |
9 |         match self.state().read()?.state() {
  |                                  ^ the trait `From<PoisonError<RwLockReadGuard<'_, GlobalState>>>` is not implemented for `WindowError`, which is required by `Result<(), WindowError>: FromResidual<Result<Infallible, PoisonError<RwLockReadGuard<'_, GlobalState>>>>`
  |
  = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
  = help: the trait `From<PoisonError<GlobalState>>` is implemented for `WindowError`
  = help: for that trait implementation, expected `GlobalState`, found `RwLockReadGuard<'_, GlobalState>`
  = note: required for `Result<(), WindowError>` to implement `FromResidual<Result<Infallible, PoisonError<RwLockReadGuard<'_, GlobalState>>>>`

Any help ? I tried to include RwLockReadGuard in my #[from] type, but seem not the way (lifetime, etc ...)

1 Like

Your source error type is not PoisonError<GlobalState>, it's PoisonError<RwLockReadGuard<'_, GlobalState>>, as clearly pointed out by the very error message you pasted.

Yes, thiserror source errors must be 'static, because only 'static types can be used as the source (which in turn is the case because only 'static types can be downcasted).

TL;DR: you can't use a type containing a non-static lifetime as a #[from]/#[source] in thiserror.

2 Likes

PoisonError should not be wrapped and passed on. PoisonError carries the lock guard so you can ignore poison if you want, but that means that the lock is still locked as long as you hold the PoisonError, and it is probably a bad idea to do that longer than necessary — it could cause deadlock. And as you have already discovered, the lifetime makes it difficult to do anyway.

Also, PoisonError indicates that something has gone wrong previously — there was previously a panic — so it may be unwise to try to recover from, and .unwrap() may be better handling.

But if you do decide you want to include this case in your error type, the answer is to not try to put the PoisonError itself in your struct. Define a StateAccessError variant without a field, and use .map_err() or a custom From implementation to convert to that variant while dropping the PoisonError.

9 Likes

Thanks a lot to both of you. I better understand the situation now !

1 Like

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.