Search code examples
rusttraits

How to generate instance of trait for another trait


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 T is 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)?


Solution

  • impl<T: Display> Display for dyn Wrapper<T> {
      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "(wrapped {})", self.unwrap())
      }
    }
    

    implements the Display trait for a dyn Wrapper<T> trait object. This is a type of object in Rust which uses dynamic dispatch instead of monomorphization. Because trait objects are dynamically sized, you can't store them on the stack. You can, however, keep a reference of type &dyn Wrapper<T>:

    fn main() {
        let burrito = Burrito { filling: 1 };
        println!("{}", &burrito as &dyn Wrapper<_>);
    }
    

    This is obviously a little more complicated than one would expect but there's a good reason. If you could impl<T: Display, W: Wrapper<T>> for W you couldn't impl Display for Burrito<T> anymore as that would conflict with the other impl and vice versa.