How to implement a Trait for iterables over double references to the same Trait

Viewed 80

Let's suppose I have a Trait Tr, defined as such:

trait Tr {
    fn tr(&self);
}

I have the Trait implemented for some type, say X:

struct X;

impl Tr for X {
    fn tr(self) {}
}

Now I would like to impl Tr for any type which can be turned into an iterator over references to values implementing Tr.

I have tried this: (playground)

impl<T: IntoIterator<Item = impl Tr>> Tr for T {
    fn tr(&self) { self.into_iter().for_each(|value| value.tr()) }
}

But it doesn't compile because IntoIterator::into_iter takes self by move and my trait only receives a reference.

With that, I attempted to make the trait take self by move as well and implement it for references: (playground)

struct X;
trait Tr {
    fn tr(self);
}

impl Tr for &X {
    fn tr(self) {}
}

impl<T: IntoIterator<Item = impl Tr>> Tr for T {
    fn tr(self) { self.into_iter().for_each(|value| value.tr()) }
}

This compiles fine, but only works for collections which hold values of X. My goal is for it to work on collections holding references to X, e.g.: [&X].

To solve this, I have implemented Tr for double references to X, which works, but looks weird: (playground)

impl Tr for &&X {
    fn tr(self) {}
}

This also doesn't allow me to call tr directly on instances of X as it was possible when using impl for a single reference: X.tr() and instead I have to put X behind at least one reference to call tr on it: (&X).tr(). This can of course be resolved by adding another impl for a single reference, but it increases the complexity a lot when I have many types that implement Tr.

The question is, is this the correct way to do this in Rust? Can I simplify the code in any way or get rid of the "take self by move" altogether?

Edit: Ideally, the result should allow all of the following:

X.tr();
[&X].tr();
vec![&X].tr();
(&[&X]).tr();
(&vec![&X]).tr();
0 Answers
Related