I have the DoSomethingProvider trait that expects a parameter of one of its functions to be of trait type DoSomethingListener.

I have a concrete struct DoSomethingManager that has a member of type DoSomethingProvider, and will implement the DoSomethingListener trait and pass it self as a listener to DoSomethingProvider.

Hopefully the code will illustrate what I'm trying to do:

pub trait DoSomethingListener {
    fn something_was_done(msg: &str);
}

pub trait DoSomethingProvider<'a, T>
where
    T: DoSomethingListener,
{
    fn add_do_something_listener(listener: T);
}

/* note:  The struct below will implement DoSomethingListener, and has
 * a DoSomethingProvider field.  It will pass itself as a listener to
 * DoSomethingProvider (which listens to a message queue and notifies
 * listeners of certain events)
 */

//this doesn't work.  Compiler complains about unused type T
pub struct DoSomethingManager<'a, B, T>
where
    T: DoSomethingListener,
    B: DoSomethingProvider<'a, T>,
{
    provider: Box<B>,
    // doesn't have any member of type T
}

// ...

/* So I tried this:
 * this doesn't work.  Compiler complains that DoSomethingProvider
 * expects one type parameter
 */
pub struct DoSomethingManager<'a, B>
where
    B: DoSomethingProvider<'a>,
{
    provider: Box<B>,
    // doesn't have any member of type T
}

//...

/* this compiles, but its a hack */
pub struct DoSomethingManager<'a, B, T>
where
    T: DoSomethingListener,
    B: DoSomethingProvider<'a, T>,
{
    provider: Box<B>,
    dummy: Box<T>,
    // added unused dummy member of type T
}

I am an experienced Python developer but I'm new to Rust. What is the proper way to implement this polymorphic code in Rust?

2 Answers

0
Francis Gagné On Best Solutions

Change DoSomethingProvider to use an associated type instead of a type parameter for the listener type.

pub trait DoSomethingListener {
    fn something_was_done(msg: &str);
}

pub trait DoSomethingProvider {
    type Listener: DoSomethingListener;

    fn add_do_something_listener(listener: Self::Listener);
}

pub struct DoSomethingManager<B>
where
    B: DoSomethingProvider,
{
    provider: Box<B>,
}

Note that by using either an associated type or a type parameter, a particular instance of DoSomethingProvider will only be able to accept listeners of a single specific type. If you want to be able to register listeners of various types, you will need to use dynamic dispatch via trait objects.

0
Community On

What I got to compile is not far off of what has to be done to resolve this, but I don't like the solution. It seems you do have to add a dummy field that uses the type with PhantomData.