Issue with iterator monomorphization in a struct

Hello everyone!

I am relying on iterators to perform some streaming processing. I have an issue where I have to store the result of a filter_map in a struct. For this purpose, I have used generics. You can have a minimal example of my issue in this playground. It seems that it cannot conclude that the result of my filter_map (the FilterMap struct) is a u32 iterator, even though .next() returns a Option<u32> as expected from a u32 iterator (let test: Option<u32> = new_iter.next(); does not make any compilation error).

As a temporary fix, I have stored the Iterator as a Box<Iterator<Item=u32>>, which works just fine. However, I am concerned about some sort of performance hit since I am relying on dynamic function dispatch on a pretty hot path.

Am I doing something wrong? What can I do to fix it? In my program, the iterator should stay in the struct but I am open to suggestions.

Return IteratorContainer<impl Iterator<Item = u32>> from new. This says, "I am returning an Iterator, but I'm not going to say exactly what type it is, just that it yields u32". This won't be a performance hit because the compiler knows what type it is, but you can't rely on anything beside it being an Iterator

@RustyYato I don't think that would work in this case.

struct IteratorContainer<I> {
    iter: I,
}

impl<I> IteratorContainer<I> {
    fn new<J: Iterator<Item=i16>>(iter: J) -> IteratorContainer<impl Iterator<Item=u32>> {
        IteratorContainer {
            iter: iter.filter_map(|it| if it < 0 { None } else { Some(it as u32) })
                      .skip(1),
        }
    }
}

fn main() {
    let iterator = -2..4;
    let container = IteratorContainer::new(iterator);
}

The compiler complains (correctly) that it can't figure out what I is.

error[E0282]: type annotations needed for `IteratorContainer<impl std::iter::Iterator>`
  --> src/main.rs:22:21
   |
22 |     let container = IteratorContainer::new(iterator);
   |         ---------   ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for `I`
   |         |
   |         consider giving `container` the explicit type `IteratorContainer<impl std::iter::Iterator>`, where the type parameter `I` is specified

error: aborting due to previous error

I think you'd need to make this a free function instead of an associated function of IteratorContainer<I>. That way you don't need to specify what I is.

fn new_iterator_container(
    iter: impl Iterator<Item = i16>,
) -> IteratorContainer<impl Iterator<Item = u32>> {
    IteratorContainer {
        iter: iter.filter_map(|it| if it < 0 { None } else { Some(it as u32) })
                  .skip(1),
    }
}
1 Like

Indeed, but with the trait_alias_impl_trait (existential types) feature, it will be possible:

#![feature(type_alias_impl_trait)]

struct IteratorContainer<J> {
    iter: Iter<J>,
}
type Iter<J> = impl Iterator<Item = u32>;

impl<J> IteratorContainer<J>
where
    J : Iterator<Item = i32>,
{
    fn new (iter: J) -> Self
    {
        let iter: Iter<J> = {
            // type_alias_impl_trait requires free functions 
            // to "define" what the type alias actually is.
            #[allow(bad_style)]
            #[inline]
            fn mk_Iter<J> (iter: J) -> Iter<J>
            where
                J : Iterator<Item = i32>,
            {
                iter.filter_map(|it| {
                        if it < 0 {
                            None
                        } else {
                            Some(it as u32)
                        }
                    })
                    .skip(1)
            }
            mk_Iter(iter)
        };
        Self { iter }
    }
}
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.