Trait objects with associated types

Yes, this is the pattern that I've been trying to find. Thanks @ogeon!

It seems that the way one leaves an associated type t abstract is by forgetting about it altogether. In particular, one can introduce a new trait that omits the associated type t entirely. Then, one can write a conversion that works universally. I use trait S below for this purpose (@ogeon's suggestion above uses trait AnyT for this purpose).

Here's that suggested pattern applied to my earlier example:

trait T : Debug {   // The same as before
    type t;
    fn get (self:&Self) -> Self::t ;
    fn doit (self:&Self, Self::t ) -> String ;
}

trait S : Debug { // S is implemented for all T below
    fn doit (self:&Self) -> String ;
}

#[derive(Debug)]
struct Packed {
    two : (Box<S>,Box<S>)  // Use trait S here, not trait T
}

impl <A, B: T<t=A>> S for B { // Convert any T object into an S object
    fn doit (self:&Self) -> String {
        self.doit ( self.get () )
    }
}

In sum, I've added trait S, which is similar to T except that associated type T::t is absent.

This pattern is satisfying since it provides three useful views of associated type t in trait T:

  1. The concrete view in Foo and in Bar, where t is known.

  2. The abstract view of t, e.g., within the implementation of S for any trait T. In this case, t is known, but it is abstract.

  3. The "forgetful" view provided by S, where T::t is absent, and thus any differences in type T::t are irrelevant. In this example, boxed versions of Foo::Foo and Bar::Bar can be given a common type Box<S>. Hence, they can be placed into data structures such as arrays and lists, etc.

This makes sense now. Thanks again!

3 Likes