I'm writing a new crate, and I want it to be useable with any implementation of a trait (defined in another crate). The trait looks something like this:
pub trait Trait {
type Error;
...
}
I have my own Error type, but sometimes I just want to forward the underlying error unmodified. My instinct is to define a type like this:
pub enum Error<T: Trait> {
TraitError(T::Error),
...
}
This is similar to the pattern encouraged by thiserror, and appears to be idiomatic. It works fine, but I also want to use ? in my implementation, so I need to implement From:
impl<T: Trait> From<T::Error> for Error<T> {
fn from(e: T::Error) -> Self { Self::TraitError(e) }
}
That fails, because it conflicts with impl<T> core::convert::From<T> for T. I think I understand why — some other implementor of Trait could set type Error = my_crate::Error such that both impls would apply — but how else can I achieve similar semantics?
I've looked at a few other crates, and they seem to handle this by making their Error (or equivalent) generic over the error type itself, rather than the trait implementation. That works, of course, but:
- until we have inherent associated types, it's much more verbose. My
Tactually implements multiple traits, each with their ownErrortypes, so I'd now have to return types likeResult<..., Error<<T as TraitA>::Error, <T as TraitB>::Error>>etc; - it's arguably less expressive (because the relationship to
Traitis lost).
Is making my Error generic over the individual types the best (most idiomatic) option today?