itertools/
zip_longest.rs

1use super::size_hint;
2use std::cmp::Ordering::{Equal, Greater, Less};
3use std::iter::{Fuse, FusedIterator};
4
5use crate::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/// and wraps the elements in [`EitherOrBoth`].
12///
13/// This iterator is *fused*.
14///
15/// See [`.zip_longest()`](crate::Itertools::zip_longest) for more information.
16#[derive(Clone, Debug)]
17#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
18pub struct ZipLongest<T, U> {
19    a: Fuse<T>,
20    b: Fuse<U>,
21}
22
23/// Create a new `ZipLongest` iterator.
24pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U>
25where
26    T: Iterator,
27    U: Iterator,
28{
29    ZipLongest {
30        a: a.fuse(),
31        b: b.fuse(),
32    }
33}
34
35impl<T, U> Iterator for ZipLongest<T, U>
36where
37    T: Iterator,
38    U: Iterator,
39{
40    type Item = EitherOrBoth<T::Item, U::Item>;
41
42    #[inline]
43    fn next(&mut self) -> Option<Self::Item> {
44        match (self.a.next(), self.b.next()) {
45            (None, None) => None,
46            (Some(a), None) => Some(EitherOrBoth::Left(a)),
47            (None, Some(b)) => Some(EitherOrBoth::Right(b)),
48            (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
49        }
50    }
51
52    #[inline]
53    fn size_hint(&self) -> (usize, Option<usize>) {
54        size_hint::max(self.a.size_hint(), self.b.size_hint())
55    }
56
57    #[inline]
58    fn fold<B, F>(self, init: B, mut f: F) -> B
59    where
60        Self: Sized,
61        F: FnMut(B, Self::Item) -> B,
62    {
63        let Self { mut a, mut b } = self;
64        let res = a.try_fold(init, |init, a| match b.next() {
65            Some(b) => Ok(f(init, EitherOrBoth::Both(a, b))),
66            None => Err(f(init, EitherOrBoth::Left(a))),
67        });
68        match res {
69            Ok(acc) => b.map(EitherOrBoth::Right).fold(acc, f),
70            Err(acc) => a.map(EitherOrBoth::Left).fold(acc, f),
71        }
72    }
73}
74
75impl<T, U> DoubleEndedIterator for ZipLongest<T, U>
76where
77    T: DoubleEndedIterator + ExactSizeIterator,
78    U: DoubleEndedIterator + ExactSizeIterator,
79{
80    #[inline]
81    fn next_back(&mut self) -> Option<Self::Item> {
82        match self.a.len().cmp(&self.b.len()) {
83            Equal => match (self.a.next_back(), self.b.next_back()) {
84                (None, None) => None,
85                (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
86                // These can only happen if .len() is inconsistent with .next_back()
87                (Some(a), None) => Some(EitherOrBoth::Left(a)),
88                (None, Some(b)) => Some(EitherOrBoth::Right(b)),
89            },
90            Greater => self.a.next_back().map(EitherOrBoth::Left),
91            Less => self.b.next_back().map(EitherOrBoth::Right),
92        }
93    }
94
95    fn rfold<B, F>(self, mut init: B, mut f: F) -> B
96    where
97        F: FnMut(B, Self::Item) -> B,
98    {
99        let Self { mut a, mut b } = self;
100        let a_len = a.len();
101        let b_len = b.len();
102        match a_len.cmp(&b_len) {
103            Equal => {}
104            Greater => {
105                init = a
106                    .by_ref()
107                    .rev()
108                    .take(a_len - b_len)
109                    .map(EitherOrBoth::Left)
110                    .fold(init, &mut f)
111            }
112            Less => {
113                init = b
114                    .by_ref()
115                    .rev()
116                    .take(b_len - a_len)
117                    .map(EitherOrBoth::Right)
118                    .fold(init, &mut f)
119            }
120        }
121        a.rfold(init, |acc, item_a| {
122            f(acc, EitherOrBoth::Both(item_a, b.next_back().unwrap()))
123        })
124    }
125}
126
127impl<T, U> ExactSizeIterator for ZipLongest<T, U>
128where
129    T: ExactSizeIterator,
130    U: ExactSizeIterator,
131{
132}
133
134impl<T, U> FusedIterator for ZipLongest<T, U>
135where
136    T: Iterator,
137    U: Iterator,
138{
139}