]> git.lizzy.rs Git - rust.git/blob - library/core/src/iter/adapters/array_chunks.rs
Rollup merge of #100559 - nnethercote:parser-simplifications, r=compiler-errors
[rust.git] / library / core / src / iter / adapters / array_chunks.rs
1 use crate::array;
2 use crate::iter::{ByRefSized, FusedIterator, Iterator};
3 use crate::ops::{ControlFlow, NeverShortCircuit, Try};
4
5 /// An iterator over `N` elements of the iterator at a time.
6 ///
7 /// The chunks do not overlap. If `N` does not divide the length of the
8 /// iterator, then the last up to `N-1` elements will be omitted.
9 ///
10 /// This `struct` is created by the [`array_chunks`][Iterator::array_chunks]
11 /// method on [`Iterator`]. See its documentation for more.
12 #[derive(Debug, Clone)]
13 #[must_use = "iterators are lazy and do nothing unless consumed"]
14 #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
15 pub struct ArrayChunks<I: Iterator, const N: usize> {
16     iter: I,
17     remainder: Option<array::IntoIter<I::Item, N>>,
18 }
19
20 impl<I, const N: usize> ArrayChunks<I, N>
21 where
22     I: Iterator,
23 {
24     #[track_caller]
25     pub(in crate::iter) fn new(iter: I) -> Self {
26         assert!(N != 0, "chunk size must be non-zero");
27         Self { iter, remainder: None }
28     }
29
30     /// Returns an iterator over the remaining elements of the original iterator
31     /// that are not going to be returned by this iterator. The returned
32     /// iterator will yield at most `N-1` elements.
33     #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
34     #[inline]
35     pub fn into_remainder(self) -> Option<array::IntoIter<I::Item, N>> {
36         self.remainder
37     }
38 }
39
40 #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
41 impl<I, const N: usize> Iterator for ArrayChunks<I, N>
42 where
43     I: Iterator,
44 {
45     type Item = [I::Item; N];
46
47     #[inline]
48     fn next(&mut self) -> Option<Self::Item> {
49         self.try_for_each(ControlFlow::Break).break_value()
50     }
51
52     #[inline]
53     fn size_hint(&self) -> (usize, Option<usize>) {
54         let (lower, upper) = self.iter.size_hint();
55
56         (lower / N, upper.map(|n| n / N))
57     }
58
59     #[inline]
60     fn count(self) -> usize {
61         self.iter.count() / N
62     }
63
64     fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
65     where
66         Self: Sized,
67         F: FnMut(B, Self::Item) -> R,
68         R: Try<Output = B>,
69     {
70         let mut acc = init;
71         loop {
72             match self.iter.next_chunk() {
73                 Ok(chunk) => acc = f(acc, chunk)?,
74                 Err(remainder) => {
75                     // Make sure to not override `self.remainder` with an empty array
76                     // when `next` is called after `ArrayChunks` exhaustion.
77                     self.remainder.get_or_insert(remainder);
78
79                     break try { acc };
80                 }
81             }
82         }
83     }
84
85     fn fold<B, F>(mut self, init: B, f: F) -> B
86     where
87         Self: Sized,
88         F: FnMut(B, Self::Item) -> B,
89     {
90         self.try_fold(init, NeverShortCircuit::wrap_mut_2(f)).0
91     }
92 }
93
94 #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
95 impl<I, const N: usize> DoubleEndedIterator for ArrayChunks<I, N>
96 where
97     I: DoubleEndedIterator + ExactSizeIterator,
98 {
99     #[inline]
100     fn next_back(&mut self) -> Option<Self::Item> {
101         self.try_rfold((), |(), x| ControlFlow::Break(x)).break_value()
102     }
103
104     fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
105     where
106         Self: Sized,
107         F: FnMut(B, Self::Item) -> R,
108         R: Try<Output = B>,
109     {
110         // We are iterating from the back we need to first handle the remainder.
111         self.next_back_remainder();
112
113         let mut acc = init;
114         let mut iter = ByRefSized(&mut self.iter).rev();
115
116         // NB remainder is handled by `next_back_remainder`, so
117         // `next_chunk` can't return `Err` with non-empty remainder
118         // (assuming correct `I as ExactSizeIterator` impl).
119         while let Ok(mut chunk) = iter.next_chunk() {
120             // FIXME: do not do double reverse
121             //        (we could instead add `next_chunk_back` for example)
122             chunk.reverse();
123             acc = f(acc, chunk)?
124         }
125
126         try { acc }
127     }
128
129     fn rfold<B, F>(mut self, init: B, f: F) -> B
130     where
131         Self: Sized,
132         F: FnMut(B, Self::Item) -> B,
133     {
134         self.try_rfold(init, NeverShortCircuit::wrap_mut_2(f)).0
135     }
136 }
137
138 impl<I, const N: usize> ArrayChunks<I, N>
139 where
140     I: DoubleEndedIterator + ExactSizeIterator,
141 {
142     /// Updates `self.remainder` such that `self.iter.len` is divisible by `N`.
143     fn next_back_remainder(&mut self) {
144         // Make sure to not override `self.remainder` with an empty array
145         // when `next_back` is called after `ArrayChunks` exhaustion.
146         if self.remainder.is_some() {
147             return;
148         }
149
150         // We use the `ExactSizeIterator` implementation of the underlying
151         // iterator to know how many remaining elements there are.
152         let rem = self.iter.len() % N;
153
154         // Take the last `rem` elements out of `self.iter`.
155         let mut remainder =
156             // SAFETY: `unwrap_err` always succeeds because x % N < N for all x.
157             unsafe { self.iter.by_ref().rev().take(rem).next_chunk().unwrap_err_unchecked() };
158
159         // We used `.rev()` above, so we need to re-reverse the reminder
160         remainder.as_mut_slice().reverse();
161         self.remainder = Some(remainder);
162     }
163 }
164
165 #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
166 impl<I, const N: usize> FusedIterator for ArrayChunks<I, N> where I: FusedIterator {}
167
168 #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
169 impl<I, const N: usize> ExactSizeIterator for ArrayChunks<I, N>
170 where
171     I: ExactSizeIterator,
172 {
173     #[inline]
174     fn len(&self) -> usize {
175         self.iter.len() / N
176     }
177
178     #[inline]
179     fn is_empty(&self) -> bool {
180         self.iter.len() < N
181     }
182 }