1

I'm trying to add functions to Iterator where the associated type Item is a reference to a struct with an explicit lifetime.

When I've wanted to modify the iterator state or return a new value I've had no problems, but when I attempt to return a new Iterator where Item is a reference with an explicit lifetime, the compiler complains.

Example

use std::marker::PhantomData;

/// First, an "Inner" struct to be contained in my custom iterator
pub struct Inner {
    text: String,
}

/// Then, the "CustomIterator" in question.  Notice that `Item` is `&'a Inner`.
pub struct CustomIterator<'a, I: Iterator<Item = &'a Inner>> {
    iter: I,
    _marker: PhantomData<&'a i8>,
}

/// Implementing Iterator for CustomIterator so as to define the `next()` function, as you do...
impl<'a, I: Iterator<Item = &'a Inner>> Iterator for CustomIterator<'a, I> {
    type Item = &'a Inner;
    fn next(&mut self) -> Option<Self::Item> {
        println!("Custom next called");
        self.iter.next()
    }
}

/// Now, creating a custom trait definition called IterateMore that:
///   1. inherits Iterator
///   2. includes a default method called `more` which returns a `CustomIterator`
pub trait IterateMore<'a>: Iterator {
    type Item;
    fn more(self) -> CustomIterator<'a, Self>
    where
        Self: Sized;
}

/// Implementing `IterateMore` for an iterator of the specific type `Iterator<Item=&'a Inner>`
impl<'a, I: Iterator<Item = &'a Inner>> IterateMore<'a> for I
where
    I: Iterator,
{
    type Item = &'a Inner;
    fn more(self) -> CustomIterator<'a, Self>
    where
        Self: Sized,
    {
        CustomIterator {
            iter: self,
            _marker: PhantomData,
        }
    }
}

fn main() {
    let inner = Inner {
        text: "Hello world".to_string(),
    };
    let inners = vec![&inner];
    inners.iter().more().next();
}

(See it on repl.it)

Error

error[E0271]: type mismatch resolving `<Self as std::iter::Iterator>::Item == &'a Inner`
  --> src/main.rs:28:5
   |
28 | /     fn more(self) -> CustomIterator<'a, Self>
29 | |     where
30 | |         Self: Sized;
   | |____________________^ expected associated type, found reference
   |
   = note: expected type `<Self as std::iter::Iterator>::Item`
              found type `&'a Inner`
   = note: required by `CustomIterator`

Why is Item not being resolved here? It is a bit frustrating as the compiler also complains if I try to set &'a Inner as the default Item type in the trait definition, saying:

error: associated type defaults are unstable (see issue #29661)

How could this be fixed or done differently?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • [Working code](https://play.rust-lang.org/?gist=2efecb579b3429f2932d2b635bb3ccfa&version=stable). I can't elaborate on changes yet. – red75prime Nov 16 '17 at 13:06

1 Answers1

3

It is unclear to me why you'd want to restrict the wrapped iterator to some custom type (given that you still have to write down the restriction every time you use the type, although that might change). But perhaps your "real" next function does something funny.

  • PhantomData doesn't seem to be necessary (anymore) to "use" the lifetime when it is used in a where-clause.
  • IterateMore shouldn't have an Item associated type, given Iterator already has it. (If you'd really need a new type pick a different name)
  • As IterateMore uses the CustomIterator type it needs to repeat the requirements, in this case Iterator<Item = &'a Inner> (that is what the type mismatch error is about); this is not the same as saying type Item = &'a Inner in the trait definition.

Playground

/// an "Inner" struct to be contained in my custom iterator
pub struct Inner {
    text: String,
}

pub struct CustomIterator<'a, I>
where
    I: Iterator<Item = &'a Inner>,
{
    iter: I,
}

impl<'a, I> Iterator for CustomIterator<'a, I>
where
    I: Iterator<Item = &'a Inner>,
{
    type Item = I::Item;
    fn next(&mut self) -> Option<Self::Item> {
        println!("Custom next called");
        self.iter.next()
    }
}

pub trait IterateMore<'a>: Iterator<Item = &'a Inner> + Sized {
    fn more(self) -> CustomIterator<'a, Self>;
}

impl<'a, I> IterateMore<'a> for I
where
    I: Iterator<Item = &'a Inner>,
{
    fn more(self) -> CustomIterator<'a, Self> {
        CustomIterator { iter: self }
    }
}

fn main() {
    let inner = Inner {
        text: "Hello world".to_string(),
    };
    let inners = vec![inner];
    inners.iter().more().next();
}

You could also remove the type restrictions everywhere like this (and only add it back in the place you actually need/want it):

Playground

pub struct CustomIterator<I> {
    iter: I,
}

impl<I> Iterator for CustomIterator<I>
where
    I: Iterator,
{
    type Item = I::Item;
    fn next(&mut self) -> Option<Self::Item> {
        println!("Custom next called");
        self.iter.next()
    }
}

pub trait IterateMore: Iterator + Sized {
    fn more(self) -> CustomIterator<Self>;
}

impl<I> IterateMore for I
where
    I: Iterator,
{
    fn more(self) -> CustomIterator<Self>
    {
        CustomIterator { iter: self }
    }
}

fn main() {
    let inners = vec!["Hello world".to_string()];
    inners.iter().more().next();
}
Stefan
  • 5,304
  • 2
  • 25
  • 44
  • Thanks Stefan, I see your point about Item. Also, obliged for the generic example. I've been relying on restrictive typing because the items for this iterator will only ever be a particular struct from a crate I don't own, and it doesn't have any traits I can parameterise. I've toyed with the idea of sending a PR for that project which includes a trait for the struct in question that would contain a default definition of all its methods. For the size of my project, I'm not sure if it's worth it. – An Camchéachta Nov 16 '17 at 15:59
  • Re `PhantomData`, it may be instructive to compare with [this question](https://stackoverflow.com/q/40484154/3650362), in which `'a` is used in the bound only as a lifetime parameter (`LT: ListTerm<'a>`) and is considered "unused". Here `'a` is used as part of an associated type (`I: Iterator`) and is not considered unused (because the compiler can determine the variance of `CustomIterator` over `'a`). – trent Nov 16 '17 at 18:38
  • (Although I agree that you don't need to parameterize by `'a` in this example.) – trent Nov 16 '17 at 18:48