Returning a trait from a trait (Or all I wanted was an iterator)

Apologies for the country song subject line, but I think it's kind of catchy!

My problem has a solution that will be obvious to anyone not me. It will probably even be obvious to me, by tomorrow...

At a high level, I want a trait that has an iter() method that returns an Iterator over references. I was surprised to not find a trait for iter() in the standard library; only IntoIter is supported. But that is to return values, not references.

My first attempt looks like so: Rust Playground

The relevant code being

trait MyTrait<T> {
    fn iter(&self) -> Iterator<Item = &T>;
}

and

impl<T> MyTrait<T> for MyStruct<T> {
    fn iter(&self) -> MyStructIterator<T> {
        MyStructIterator::new(&self.elements)
    }
}

The compile error says it all; my trait returns an Iterator, but my implementation of that trait returns a specific struct named MyStructIterator (I'm very imaginative in naming). Though MyStructIterator implements Iterator, Rust doesn't seem to know that. I've tried returning Iterator, but the compiler doesn't like that, and I tried some permutations of putting dyn and impl in the trait definition and the method implementation, but I didn't know what I was doing.

My second attempt was to "do it just like IntoIterator": Rust Playground

This also almost worked, but again, the compiler didn't like this bit:

impl<'a, T> MyTrait<T> for MyStruct<T> {
    type Item = T;
    type MyIter = MyStructIterator<'a, T>;

    fn iter(&self) -> MyStructIterator<T> {
        MyStructIterator::new(&self.elements)
    }
}

The lifetime 'a isn't anchored to anything, and I don't know how to satisfy it.

So the question is: Am I getting close? :slight_smile:

IntoIterator is not just for values - whether the yielded type is a value or reference depends on what it's implemented for.

This is your example with IntoIterator - you should stick with that unless you have a good reason not to, since it'll be familiar to people and you can use the for loop against it.

4 Likes

Thank you!

That's what I thought at first, but then I saw The three forms of iteration and got confused. I had actually been trying to get a version working with IntoIterator before, but I failed when I tried to use it with subtraits. I tried again basing off your code, but I ended up with a fascinating compiler error: MyStruct<T> is not an iterator.

Which is true, but I don't understand why IntoIterator would expect MyStruct to also implement Iterator, and if it did, I don't know what a next() method on MyStruct would ought to return.

So I'm thinking that the compiler hint is incorrect in this case, and I have defined my subtrait differently.

You have:

trait MyTrait<T>: IntoIterator { ... }
impl<T> MyTrait<T> for MyStruct<T> { ... }
impl<'a, T> IntoIterator for &'a MyStruct<T> { ... }

The second line implementing MyTrait<T> fails because MyStruct<T> does not implement IntoIterator -- only &MyStruct<T> does.

You could say something like this instead:

trait MyTrait<T>
where
    for<'a> &'a Self: IntoIterator
{ ... }

But this gets annoying because only supertraits are implicitly elaborated.

This is because the compiler is trying to apply the blanket impl:

impl<I> IntoIterator for I where I: Iterator
1 Like