Dynamic use of generic traits as a struct field

Hi all,
I'm struggling around dynamic representation of generic traits. Specifically closure in my example, but I guess the example will hold for other generic traits.
Here's a simplified example:

Some trait and structs:

trait Speaks {
    fn speak(&self);
}

struct Dog {}
struct Cow {}

impl Speaks for Dog {
    fn speak(&self) {
        println!("woof");
    }
}

impl Speaks for Cow {
    fn speak(&self) {
        println!("moo");
    }
}

I want to create a struct that will hold a function that gets a "Speaker". This is my best effort:

struct Runner<F, S>
where
    S: Speaks,
    F: Fn(&S),
{
    pub f: F,
    pub m: PhantomData<S>,
}

PhantomData is needed so that generic param S is used (can this be avoided?)

Indeed I'm able to init this struct:

fn main() {
    let x = Runner {
        f: |s: Dog| s.speak(),
        m: PhantomData,
    };

    let y = Runner {
        f: |s: Cow| s.speak(),
        m: PhantomData,
    };

    (x.f)(Dog {});
    (y.f)(Cow {});
}

My next need is to create a vector containing a dynamic types of Runner:

let v: Vec<Box<Runner<dyn ???>>> = vec!(Box::new(x), Box::new(y))

I'm not sure how to proceed. How do I define the type of the vector? Must I change the definition of Runner to support such a case?

This is not possible if your Runner needs to be generic. There is no way to make a dyn Whatever with generic methods, because there's no way to put the method pointers in the vtable at compilation time (not all possible materializations of the type parameters can be known upfront).

It's not entirely clear what you want to have in the end. In particular, your two Runners contain closures which are specific to Dog and Cow. Do you want a vector of Runners which each require specific types, or do you want a vector of Runners each of which can accept any Speaks? What kind of different things are Runners going to do?

For example, this non-generic fully dynamic definition compiles (and has no PhantomData),

struct Runner {
    pub f: Box<dyn Fn(&dyn Speaks)>,
}

fn main() {
    let xy = Runner {
        f: Box::new(|s| s.speak()),
    };

    let v: Vec<Runner> = vec![xy];
}

but perhaps it's given up something you wanted.

well I hoped to be able to create Runners with specific typed parameters so that I could use the different type properties in the different Runners.

I'll try to get my head around this.

Thanks!

Perhaps a more realistic and more complete example would help us figure out a solution.

Whenever you want some code to start from a generic/dynamic context, and then do something that's specific to a type, this must be done via the interface to that type provided by some trait. (In the extreme case, that trait would be Any.) So, you may need to expand Speaks to be more expressive to support what you need to do, rather than providing only an opaque "do your thing" operation.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.