Storing Impl trait in struct

I'm trying to store the result of a function that returns an Impl Iterator in a struct. I think the only way is to use a generic type. Consider the following program:

struct Foo<'a, I: Iterator<Item = &'a u8>> {
    inner: I,
}

struct Bar<'a> {
    v: &'a [u8],
}

impl<'a> Bar<'a> {
    fn numbers(&self) -> impl Iterator<Item=&'a u8> {
        self.v.iter().filter(|&&x| x!= 4)
    }
}

impl<'a, I: Iterator<Item = &'a u8>> Iterator for Foo<'a, I> {
    type Item=&'a u8;
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }
}

impl<'a, I: Iterator<Item = &'a u8>> Foo<'a, I> {
    fn new(a: I) -> Foo<'a, I> {
        Foo { inner: a }
    }

    fn new2(a: &Bar) -> Foo<'a, I> {
        let v = a.numbers();
        Foo { inner: v }
    }
}


fn main() {
    let values = vec![0,1,2,3,4,5,6];
    let bar = Bar { v: values.as_slice()};

    let foo = Foo::new(bar.numbers());
    println!("{:?}", foo.collect::<Vec<_>>());

    //this errors
    let foo = Foo::new2(&bar);
    println!("{:?}", foo.collect::<Vec<_>>());

    //this works
    let num = bar.numbers();
    let foo = Foo { inner: num };
    println!("{:?}", foo.collect::<Vec<_>>());
}

This program doesn't compile because of an error in Foo::new2

error[E0308]: mismatched types
  --> src/main.rs:29:22
   |
29 |         Foo { inner: v }
   |                      ^ expected type parameter, found anonymized type
   |
   = note: expected type `I`
              found type `impl std::iter::Iterator`

But when I do the same assignment in main it does work. Is there a reason for this behavior?

1 Like

Examine this portion of your code:

impl<'a, I: Iterator<Item = &'a u8>> Foo<'a, I> {
    fn new2(a: &Bar) -> Foo<'a, I> {
        let v = a.numbers();
        Foo { inner: v }
    }
}

This syntax means that the code must work for any type I that implements Iterator<Item = &'a u8>. The user of this function is free to substitute I with any suitable type and the new2() function must produce a Foo<'a, I> with that I. However, the implementation of new2() is incorrect because this code can only produce one particular I type.

An attempted fix could look like this:

impl Foo<'a, impl Iterator<Item=&'a u8>> {
    fn new2(a: &Bar) -> Self {
        let v = a.numbers();
        Foo { inner: v }
    }
}

However, the compiler says that impl Trait not allowed outside of function and inherent method return types, so I guess it's not supported.

You still can write it as a free (non-associated) function:

fn new2<'a>(a: &'a Bar) -> Foo<'a, impl Iterator<Item=&'a u8>> {
    let v = a.numbers();
    Foo { inner: v }
}
1 Like

That makes sense, thank you. I was reasoning about the result of a.numbers() being always of the required Iterator type instead of producing a valid value for all possible types of I.