primitives/utils/
iter.rs

1#[macro_export]
2/// Create an iterator running multiple iterators in lockstep, panicking if they
3/// are not of the same length.
4///
5/// Length checks are done lazily as the resulting iterator is built.
6/// I.e. the `izip_eq_lazy!` iterator yields elements until any subiterator
7/// returns `None`.
8///
9/// **Note:** This macro is slower than `izip_eq!` however it allows to zip iterators of
10/// unknown or unbounded size.
11macro_rules! izip_eq_lazy {
12    // @closure creates a tuple-flattening closure for .map() call. usage:
13    // @closure partial_pattern => partial_tuple , rest , of , iterators
14    // eg. izip_eq_lazy!( @closure ((a, b), c) => (a, b, c) , dd , ee )
15    ( @closure $p:pat => $tup:expr ) => {
16        |$p| $tup
17    };
18
19    // The "b" identifier is a different identifier on each recursion level thanks to hygiene.
20    ( @closure $p:pat => ( $($tup:tt)* ) , $_iter:expr $( , $tail:expr )* ) => {
21        $crate::izip_eq_lazy!(@closure ($p, b) => ( $($tup)*, b ) $( , $tail )*)
22    };
23
24    // unary
25    ($first:expr $(,)*) => {
26        std::iter::IntoIterator::into_iter($first)
27    };
28
29    // binary
30    ($first:expr, $second:expr $(,)*) => {
31        itertools::zip_eq(
32            std::iter::IntoIterator::into_iter($first),
33            $second,
34        )
35    };
36
37    // n-ary where n > 2
38    ( $first:expr $( , $rest:expr )* $(,)* ) => {
39        {
40            let iter = std::iter::IntoIterator::into_iter($first);
41            $(
42                let iter = itertools::zip_eq(iter, $rest);
43            )*
44            std::iter::Iterator::map(
45                iter,
46                $crate::izip_eq_lazy!(@closure a => (a) $( , $rest )*)
47            )
48        }
49    };
50}
51
52#[macro_export]
53/// Create an iterator running multiple iterators in lockstep, panicking if they
54/// are not the same length.
55///
56/// Length checks are eagerly performed before the resulting iterator is built.
57///
58/// **Note:** For performance reasons, this macro is preferable to `izip_eq_lazy!` when the lengths
59/// of the iterators are known.
60macro_rules! izip_eq {
61    (@assert_eq_len $first:expr, $second:expr) => {
62        assert!($first.len() == $second.len(), "iterator length mismatch: {} vs {}", $first.len(), $second.len());
63    };
64
65    // unary
66    ($first:expr $(,)*) => {
67        std::iter::IntoIterator::into_iter($first)
68    };
69
70    // binary
71    ($first:expr, $second:expr $(,)*) => {
72        {
73            let iter = std::iter::IntoIterator::into_iter($first);
74            let second = std::iter::IntoIterator::into_iter($second);
75            $crate::izip_eq!(@assert_eq_len iter, second);
76            let iter = std::iter::Iterator::zip(iter, second);
77            iter
78        }
79    };
80
81    // n-ary where n > 2
82    ( $first:expr $( , $rest:expr )* $(,)* ) => {
83        {
84            let iter = std::iter::IntoIterator::into_iter($first);
85            $(
86                let rest = std::iter::IntoIterator::into_iter($rest);
87                $crate::izip_eq!(@assert_eq_len iter, rest);
88                let iter = std::iter::Iterator::zip(iter, rest);
89            )*
90            std::iter::Iterator::map(
91                iter,
92                $crate::izip_eq_lazy!(@closure a => (a) $( , $rest )*)
93            )
94        }
95    };
96}
97
98#[macro_export]
99/// An adaptor for `itertools::chain!` macro which creates an ExactSizeIterator.
100///
101/// **Note:** This macro collects the output of `itertools::chain!` into a vector. This may prevent
102/// some compiler optimizations implying iterators.
103macro_rules! chain_eq {
104    ($first:expr $( , $rest:expr )* $(,)*) => {{
105        let it = itertools::chain!($first, $($rest, )*);
106        let vec = <std::vec::Vec<_> as std::iter::FromIterator<_>>::from_iter(it);
107        <std::vec::Vec<_> as std::iter::IntoIterator>::into_iter(vec)
108    }
109    };
110}
111
112#[macro_export]
113/// Create an iterator over hashmaps with the same keys. Accepts a list of keys, and the maps
114/// to be iterated over. Panics if any of the keys are not found in the maps.
115macro_rules! zip_maps {
116    ($keys:expr, $($map:expr),+ $(,)?) => {
117        $keys.into_iter().map(move |key| {
118            (
119                key,
120                $(
121                    $map.remove(&key).expect(&format!(
122                        "Key `{:?}` not found in map `{}`",
123                        key,
124                        stringify!($map),
125                    )),
126                )+
127            )
128        })
129    };
130}
131
132pub trait IntoExactSizeIterator:
133    IntoIterator<IntoIter: ExactSizeIterator<Item = <Self as IntoIterator>::Item>>
134{
135}
136
137impl<T: IntoIterator<IntoIter = S>, S: ExactSizeIterator<Item = <T as IntoIterator>::Item>>
138    IntoExactSizeIterator for T
139{
140}
141
142/// A trait for iterators that take exactly N elements, panicking if the iterator
143/// is shorter than N.
144pub trait TakeExact: Iterator {
145    /// Takes exactly `n` elements from the iterator, panicking if the iterator
146    /// is shorter than `n`. Returns an iterator that implements `ExactSizeIterator`.
147    fn take_exact(self, n: usize) -> TakeExactIter<Self>
148    where
149        Self: Sized,
150    {
151        TakeExactIter {
152            iter: self,
153            remaining: n,
154        }
155    }
156}
157
158impl<I: Iterator> TakeExact for I {}
159
160/// An iterator that takes exactly N elements from the underlying iterator.
161#[derive(Clone, Debug)]
162pub struct TakeExactIter<I> {
163    iter: I,
164    remaining: usize,
165}
166
167impl<I: Iterator> Iterator for TakeExactIter<I> {
168    type Item = I::Item;
169
170    fn next(&mut self) -> Option<Self::Item> {
171        if self.remaining == 0 {
172            return None;
173        }
174        self.remaining -= 1;
175        match self.iter.next() {
176            Some(item) => Some(item),
177            None => panic!("iterator shorter than expected length"),
178        }
179    }
180
181    fn size_hint(&self) -> (usize, Option<usize>) {
182        (self.remaining, Some(self.remaining))
183    }
184}
185
186impl<I: Iterator> ExactSizeIterator for TakeExactIter<I> {}
187
188#[cfg(test)]
189mod tests {
190    use crate::{izip_eq, izip_eq_lazy, utils::TakeExact};
191
192    #[test]
193    fn test_izip_eq() {
194        let a = [1, 2, 3];
195        let b = [4, 5, 6];
196        let c = [7, 8, 9];
197
198        {
199            let mut results = [0, 0, 0];
200            for (r, aa, bb, cc) in izip_eq_lazy!(&mut results, &a, &b, &c) {
201                *r = aa + bb + cc;
202            }
203
204            assert_eq!(results, [1 + 4 + 7, 2 + 5 + 8, 3 + 6 + 9]);
205        }
206        {
207            let mut results = [0, 0, 0];
208            for (r, aa, bb) in izip_eq_lazy!(&mut results, &a, &b) {
209                *r = aa + bb;
210            }
211
212            assert_eq!(results, [1 + 4, 2 + 5, 3 + 6]);
213        }
214        {
215            let mut results = [0, 0, 0];
216            for (r, aa) in izip_eq_lazy!(&mut results, &a) {
217                *r = *aa;
218            }
219
220            assert_eq!(results, [1, 2, 3]);
221        }
222        {
223            let mut result = 0;
224            for aa in izip_eq_lazy!(&a) {
225                result += *aa;
226            }
227
228            assert_eq!(result, 1 + 2 + 3);
229        }
230        {
231            let mut results = [0, 0, 0];
232            for (r, aa, bb, cc) in izip_eq!(&mut results, &a, &b, &c) {
233                *r = aa + bb + cc;
234            }
235
236            assert_eq!(results, [1 + 4 + 7, 2 + 5 + 8, 3 + 6 + 9]);
237        }
238        {
239            let mut results = [0, 0, 0];
240            for (r, aa, bb) in izip_eq!(&mut results, &a, &b) {
241                *r = aa + bb;
242            }
243
244            assert_eq!(results, [1 + 4, 2 + 5, 3 + 6]);
245        }
246        {
247            let mut results = [0, 0, 0];
248            for (r, aa) in izip_eq!(&mut results, &a) {
249                *r = *aa;
250            }
251
252            assert_eq!(results, [1, 2, 3]);
253        }
254        {
255            let mut result = 0;
256            for aa in izip_eq!(&a) {
257                result += *aa;
258            }
259
260            assert_eq!(result, 1 + 2 + 3);
261        }
262    }
263
264    #[test]
265    #[should_panic(expected = "itertools: .zip_eq() reached end of one iterator before the other")]
266    fn test_izip_eq_lazy_panic() {
267        let a = [1, 2, 3];
268        let b = [4, 5];
269        let c = [7, 8, 9];
270
271        let mut results = [0, 0, 0];
272        for (r, aa, bb, cc) in izip_eq_lazy!(&mut results, &a, &b, &c) {
273            *r = aa + bb + cc;
274        }
275        unreachable!()
276    }
277
278    #[test]
279    #[should_panic(expected = "itertools: .zip_eq() reached end of one iterator before the other")]
280    fn test_izip_eq_lazy_panic_2() {
281        let a = [1, 2, 3];
282        let b = [4, 5, 6];
283        let c = [7, 8, 9];
284
285        let mut results = [0, 0];
286        for (r, aa, bb, cc) in izip_eq_lazy!(&mut results, &a, &b, &c) {
287            *r = aa + bb + cc;
288        }
289        unreachable!()
290    }
291
292    #[test]
293    #[should_panic(expected = "iterator length mismatch: 3 vs 2")]
294    fn test_izip_eq_eager_panic() {
295        let a = [1, 2, 3];
296        let b = [4, 5];
297        let c = [7, 8, 9];
298
299        let mut results = [0, 0, 0];
300        for (r, aa, bb, cc) in izip_eq!(&mut results, &a, &b, &c) {
301            *r = aa + bb + cc;
302        }
303        unreachable!()
304    }
305
306    #[test]
307    #[should_panic(expected = "iterator length mismatch: 2 vs 3")]
308    fn test_izip_eq_eager_panic_2() {
309        let a = [1, 2, 3];
310        let b = [4, 5, 6];
311        let c = [7, 8, 9];
312
313        let mut results = [0, 0];
314        for (r, aa, bb, cc) in izip_eq!(&mut results, &a, &b, &c) {
315            *r = aa + bb + cc;
316        }
317        unreachable!()
318    }
319
320    #[test]
321    #[should_panic(expected = "iterator length mismatch: 2 vs 3")]
322    fn test_izip_eq_eager_panic_3() {
323        let a = [1, 2, 3];
324
325        let mut results = [0, 0];
326        for (r, aa) in izip_eq!(&mut results, &a) {
327            *r = *aa;
328        }
329        unreachable!()
330    }
331
332    #[test]
333    #[should_panic(expected = "iterator length mismatch: 3 vs 2")]
334    fn test_izip_eq_eager_panic_4() {
335        let a = [1, 2];
336
337        let mut results = [0, 0, 0];
338        for (r, aa) in izip_eq!(&mut results, &a) {
339            *r = *aa;
340        }
341        unreachable!()
342    }
343
344    #[test]
345    fn test_zip_maps() {
346        let keys = vec![1, 2, 3];
347        let mut map1: std::collections::HashMap<_, _> = [(1, "a"), (2, "b"), (3, "c")].into();
348        let mut map2: std::collections::HashMap<_, _> = [(1, "x"), (2, "y"), (3, "z")].into();
349
350        let result: Vec<_> = zip_maps!(keys, map1, map2).collect();
351
352        assert_eq!(result, vec![(1, "a", "x"), (2, "b", "y"), (3, "c", "z"),]);
353    }
354
355    #[test]
356    #[should_panic(expected = "Key `4` not found in map `map1`")]
357    fn test_zip_maps_missing_key_panic() {
358        let keys = vec![1, 2, 3, 4];
359        let mut map1: std::collections::HashMap<_, _> = [(1, "a"), (2, "b"), (3, "c")].into();
360        let mut map2: std::collections::HashMap<_, _> = [(1, "x"), (2, "y"), (3, "z")].into();
361
362        let _result: Vec<_> = zip_maps!(keys, map1, map2).collect();
363    }
364
365    #[test]
366    fn test_take_exact() {
367        let v = vec![1, 2, 3, 4, 5];
368        let mut iter = v.into_iter().take_exact(3);
369
370        assert_eq!(iter.len(), 3);
371        assert_eq!(iter.next(), Some(1));
372        assert_eq!(iter.len(), 2);
373        assert_eq!(iter.next(), Some(2));
374        assert_eq!(iter.len(), 1);
375        assert_eq!(iter.next(), Some(3));
376        assert_eq!(iter.len(), 0);
377        assert_eq!(iter.next(), None);
378    }
379
380    #[test]
381    #[should_panic(expected = "iterator shorter than expected length")]
382    fn test_take_exact_panic() {
383        let v = vec![1, 2];
384        let mut iter = v.into_iter().take_exact(3);
385
386        assert_eq!(iter.next(), Some(1));
387        assert_eq!(iter.next(), Some(2));
388        iter.next(); // This should panic
389    }
390
391    #[test]
392    fn test_take_exact_collect() {
393        let v = vec![1, 2, 3, 4, 5];
394        let result: Vec<_> = v.into_iter().take_exact(3).collect();
395        assert_eq!(result, vec![1, 2, 3]);
396    }
397
398    #[test]
399    fn test_take_exact_size_hint() {
400        let v = vec![1, 2, 3, 4, 5];
401        let iter = v.into_iter().take_exact(3);
402        assert_eq!(iter.size_hint(), (3, Some(3)));
403    }
404}