How can I make this function more generic?

Hi everyone,

I am writing a trait and a function, that look like this:

trait MyTrait {
    fn foo(&self);
}

fn do_stuff<C, V>(collection: C)
where
    C: IntoIterator<Item = V>,
    V: MyTrait
{ /* snip */ }

The following works fine of course:

struct Thing {}
impl MyTrait for Thing { /* snip */ }

let values  = vec![Thing {}, Thing {}];
do_stuff(values);

However, I want to also accept slightly different variants of that vector as arguments.

let values: Vec<Box<dyn MyTrait>> = /* snip */;
do_stuff(values); // doesn't compile

let values: Vec<Thing> = /* snip */;
do_stuff(&values); // doesn't compile

let values: Vec<&Thing> = /* snip */;
do_stuff(values); // doesn't compile

Is there a way to make do_stuff generic over all of these different variants?
Thanks in advance!

I think you can have it take a Vec<impl AsRef<Thing>>. Or maybe impl IntoIterator<Item: AsRef<Thing>>.

I’m on my phone so can’t give detailed examples.

Why not just add missing implementations? I'm not sure what Value is here, but you can just implement MyTrait for Box<dyn Value>. As for references, if all methods of your trait accept a reference, you can write a blanket implementation that covers all references to types implementing MyTrait:

impl<'a, T: MyTrait> MyTrait for &'a T {
    fn foo(&self) {
        <T as MyTrait>::foo(self)
    }
}

The blanket implementation for references works. Thank you for that.

However, the Box<dyn Value> was just a typo, sorry.
What i meant was the following:

let values: Vec<Box<dyn MyTrait>> = /* snip */;
do_stuff(values); // doesn't compile

The compiler tells me, Box<dyn MyTrait> does not implement MyTrait.
Trying to implement that, just results in infinite recursion for me.

Try this (supercedes &’a impl):

impl<Ptr: Deref<Target=T>, T: MyTrait> MyTrait for Ptr {
    fn foo(&self) {
        <T as MyTrait>::foo(self.deref())
    }
}

I have now found a similar solution for the Box<dyn MyTrait> case:

impl<T: MyTrait + ?Sized> MyTrait for Box<T> {
    fn foo(&self) {
        <T as MyTrait>::foo(&*self)
    }
}

Thanks again @Riateche! Your answer definitely helped me to find the solutions here.

Thank you, @2e71828. Your solution is even simpler and solves both cases.
I only had to change T: MyTrait to T: MyTrait + ?Sized, to make it generic over both Box<dyn Trait> and references.

1 Like

You have to be careful to call the implementation of the inner type, not the current implementation. For example, for Box<dyn MyTrait> it can be done like this:

impl MyTrait for Box<dyn MyTrait> {
    fn foo(&self) {
        (**self).foo();
    }
}

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.