TL;DR: What's the accepted way to impl a trait for all types implementing another trait in Rust?
I'm working through trying to understand Rust's type system and coming to a bit of a hangup. I've boiled the problem down, and realized that I'm basically just trying to translate the following thing I can write in Haskell to Rust:
class Wrapper s where
unwrap :: s t -> t
data Burrito t = Burrito t
instance Wrapper Burrito where
unwrap (Burrito inner) = inner
instance (Wrapper w, Show t) => Show (w t) where
show w = "(wrapped " ++ show (unwrap w) ++ ")"
main :: IO ()
main =
print . Burrito $ 1
I was able to generally translate this as follows:
trait Wrapper<T> {
fn unwrap(&self) -> T;
}
struct Burrito<T> { filling: T }
impl<T: Copy> Wrapper<T> for Burrito<T> {
fn unwrap(&self) -> T { self.filling }
}
impl<T: Display, W: Wrapper<T>> Display for W {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(wrapped {})", self.unwrap())
}
}
fn main() {
let burrito = Burrito { filling: 1 };
println!("{}", burrito);
}
But this errors on the T type parameter in the Display impl with error:
the type parameter
Tis not constrained by the impl trait, self type, or predicates unconstrained type parameter
The other alternative I tried was doing (as suggested by my IDE):
impl<T: Display> Display for dyn Wrapper<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "(wrapped {})", self.unwrap())
}
}
but this now raises an error on the actual println in main (when done as println!("{}", burrito as Wrapper<i32>)), that there is a cast to an unsized type.
Is there a canonical way to create an impl for a trait (Display here) for all types implementing another trait (Wrapper<T> here)?
implyou can doprintln!("{}", &burrito as &dyn Wrapper<_>);(note the&s), avoiding casting to an unsized type. Rust makes this hard to do because you might want to implDisplayforBurrito<T>at some point which would conflict with the impl forWrapper.impl<T: Display, W: Wrapper<T>> Display for Wwould conflict with a normalimpl Displayfor typeW, if it already existed or was added later on. That kind of thing would also allow you to implement an external trait for an external type, just by going through a trait you own.