itertools/
zip_longest.rs

1use std::cmp::Ordering::{Equal, Greater, Less};
2use super::size_hint;
3use std::iter::Fuse;
4
5use either_or_both::EitherOrBoth;
6
7// ZipLongest originally written by SimonSapin,
8// and dedicated to itertools https://github.com/rust-lang/rust/pull/19283
9
10/// An iterator which iterates two other iterators simultaneously
11///
12/// This iterator is *fused*.
13///
14/// See [`.zip_longest()`](../trait.Itertools.html#method.zip_longest) for more information.
15#[derive(Clone, Debug)]
16#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
17pub struct ZipLongest<T, U> {
18    a: Fuse<T>,
19    b: Fuse<U>,
20}
21
22/// Create a new `ZipLongest` iterator.
23pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U> 
24    where T: Iterator,
25          U: Iterator
26{
27    ZipLongest {
28        a: a.fuse(),
29        b: b.fuse(),
30    }
31}
32
33impl<T, U> Iterator for ZipLongest<T, U>
34    where T: Iterator,
35          U: Iterator
36{
37    type Item = EitherOrBoth<T::Item, U::Item>;
38
39    #[inline]
40    fn next(&mut self) -> Option<Self::Item> {
41        match (self.a.next(), self.b.next()) {
42            (None, None) => None,
43            (Some(a), None) => Some(EitherOrBoth::Left(a)),
44            (None, Some(b)) => Some(EitherOrBoth::Right(b)),
45            (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
46        }
47    }
48
49    #[inline]
50    fn size_hint(&self) -> (usize, Option<usize>) {
51        size_hint::max(self.a.size_hint(), self.b.size_hint())
52    }
53}
54
55impl<T, U> DoubleEndedIterator for ZipLongest<T, U>
56    where T: DoubleEndedIterator + ExactSizeIterator,
57          U: DoubleEndedIterator + ExactSizeIterator
58{
59    #[inline]
60    fn next_back(&mut self) -> Option<Self::Item> {
61        match self.a.len().cmp(&self.b.len()) {
62            Equal => match (self.a.next_back(), self.b.next_back()) {
63                (None, None) => None,
64                (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
65                // These can only happen if .len() is inconsistent with .next_back()
66                (Some(a), None) => Some(EitherOrBoth::Left(a)),
67                (None, Some(b)) => Some(EitherOrBoth::Right(b)),
68            },
69            Greater => self.a.next_back().map(EitherOrBoth::Left),
70            Less => self.b.next_back().map(EitherOrBoth::Right),
71        }
72    }
73}
74
75impl<T, U> ExactSizeIterator for ZipLongest<T, U>
76    where T: ExactSizeIterator,
77          U: ExactSizeIterator
78{}