Iterating with a cloned iterator


#1

Short question

Does already exist an iterator extension that returns a tuple with the Item and the cloned iterator?

Long explanation

I was doing something pretty trivial, a nested iteration to take pairs of element in order to get the combinations. This can be easily done with the following:

let data: Vec<_> = (1..=10).collect();

let mut first_iter = data.iter();
while let Some(first) = first_iter.next() {
    first_iter.clone().for_each(|second| {
        println!("{}-{}", first, second);
    })
}

Unfortunately, my brain really wants to use a more functional approach, something similar to the following:

let mut first_iter = data.iter();
first_iter.by_ref().for_each(|first| {
    first_iter.clone().for_each(|second| {
        println!("{}-{}", first, second);
    });
});

This does not compile, just because first_iter is mutable borrowed by the outer loop, and it cannot be borrowed again to create a clone.

Said that, I thought that an iterator extension to take an element and the clone of the iterator would be trivial. Something like this:

trait WithIterExt: Iterator + Clone {
    fn with_iter(self) -> WithIter<Self> {
        WithIter(self)
    }
}

struct WithIter<Inner>(Inner);

impl<Inner> Iterator for WithIter<Inner>
where
    Inner: Iterator + Clone,
{
    type Item = (<Inner as Iterator>::Item, Inner);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|v| (v, self.0.clone()))
    }
}

impl<I: Iterator + Clone> WithIterExt for I {}

This can be used in a very straightforward and rustic way:

data.into_iter().with_iter().for_each(|(v1, iter)| {
    iter.for_each(|v2| {
        println!("{}-{}", v1, v2);
    });
});

My point is… it is too simple to not be around in some crate. I looked in itertools but I did not find it. Do you know if this has been implemented somewhere? If not, maybe I will just open a PR for itertools.
What do you think?


#2

Cloning iterator is not always possible in reasonable complexity, so I don’t think there is standard general solution (note, that Iterator trait doesn’t require Clone). In itertools there is this tee function which can be use for such case (it behaves somehow as iterator cloning, but it might be very expensive). However I don’t think generic solutions which tees iterator every iteration step, because of its cost. I think that multipeek could have some usage here - you create peekable iterator, you iterate over it with while let Some(item) = iter.next(), and for the inner loop you just peek over iterator (but I don’t know functional generic solution for this).


#3

I am a bit in disagreement with you. I know that Clone-ing an iterator can be expensive, but most of the time it is not. Moreover, this approach does exactly the same as the “two nested loops with a clone inside”, it is just a different way of writing the thing.

Nevertheless, thanks for pointing out tee, but in this case it is not particularly useful because you will still resemble the example I made with the while let pattern – just with tee instead of clone.


#4

Itertools already has tuple_combinations() for this use case. There’s also combinations() if you need more items and can tolerate a Vec.

Your with_iter() would offer more general flexibility though.


#5

Thank you @cupiver ! Honestly, I focused on the “iterator” problem instead of just searching for a combinator. For my case, both tuple_combinations and combinations are exactly what I needed.

Now I am unsure about the real usefulness of my with_iter impl. If someone finds a practical application, just say it. :smile: