• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Provides the `peeking_take_while` iterator adaptor method.
2 //!
3 //! The `peeking_take_while` method is very similar to `take_while`, but behaves
4 //! differently when used with a borrowed iterator (perhaps returned by
5 //! `Iterator::by_ref`).
6 //!
7 //! `peeking_take_while` peeks at the next item in the iterator and runs the
8 //! predicate on that peeked item. This avoids consuming the first item yielded
9 //! by the underlying iterator for which the predicate returns `false`. On the
10 //! other hand, `take_while` will consume that first item for which the
11 //! predicate returns `false`, and it will be lost.
12 //!
13 //! In case the closure may have side effects, it could be necessary to apply
14 //! [`fuse`](Iterator::fuse) on the returned iterator, to prevent the predicate
15 //! from being called after it first returned `false`.
16 //!
17 //! ```
18 //! // Bring the `peeking_take_while` method for peekable iterators into
19 //! // scope.
20 //! use peeking_take_while::PeekableExt;
21 //!
22 //! # fn main() {
23 //! // Let's say we have two collections we want to iterate through: `xs` and
24 //! // `ys`. We want to perform one operation on all the leading contiguous
25 //! // elements that match some predicate, and a different thing with the rest of
26 //! // the elements. With the `xs`, we will use the normal `take_while`. With the
27 //! // `ys`, we will use `peeking_take_while`.
28 //!
29 //! let xs: Vec<u8> = (0..100).collect();
30 //! let ys = xs.clone();
31 //!
32 //! let mut iter_xs = xs.into_iter();
33 //! let mut iter_ys = ys.into_iter().peekable();
34 //!
35 //! {
36 //!     // Let's do one thing with all the items that are less than 10.
37 //! #   fn do_things_with<T>(_: T) {}
38 //!
39 //!     let xs_less_than_ten = iter_xs.by_ref().take_while(|x| *x < 10);
40 //!     for x in xs_less_than_ten {
41 //!         do_things_with(x);
42 //!     }
43 //!
44 //!     let ys_less_than_ten = iter_ys.by_ref().peeking_take_while(|y| *y < 10);
45 //!     for y in ys_less_than_ten {
46 //!         do_things_with(y);
47 //!     }
48 //! }
49 //!
50 //! // And now we will do some other thing with the items that are greater than
51 //! // or equal to 10.
52 //!
53 //! // ...except, when using plain old `take_while` we lost 10!
54 //! assert_eq!(iter_xs.next(), Some(11));
55 //!
56 //! // However, when using `peeking_take_while` we did not! Great!
57 //! assert_eq!(iter_ys.next(), Some(10));
58 //! # }
59 //! ```
60 
61 #![no_std]
62 #![forbid(
63     clippy::as_conversions,
64     clippy::cast_ptr_alignment,
65     missing_docs,
66     trivial_casts,
67     unsafe_code
68 )]
69 
70 use core::fmt;
71 
72 /// The `Iterator` extension trait that provides the `peeking_take_while`
73 /// method.
74 ///
75 /// See the [module documentation](./index.html) for details.
76 pub trait PeekableExt<I>: Iterator
77 where
78     I: Iterator,
79 {
80     /// The `peeking_take_while` method is very similar to `take_while`, but behaves
81     /// differently when used with a borrowed iterator (perhaps returned by
82     /// `Iterator::by_ref`).
83     ///
84     /// `peeking_take_while` peeks at the next item in the iterator and runs the
85     /// predicate on that peeked item. This avoids consuming the first item yielded
86     /// by the underlying iterator for which the predicate returns `false`. On the
87     /// other hand, `take_while` will consume that first item for which the
88     /// predicate returns `false`, and it will be lost.
89     ///
90     /// In contrast to `take_while`, iterating the iterator might call the predicate again
91     /// after it first returned `false` (the returned iterator isn't fused).
92     /// If that is not intended, calling [`fuse`](Iterator::fuse) on the returned iterator
93     /// prevents that.
peeking_take_while<P>(&mut self, predicate: P) -> PeekingTakeWhile<'_, I, P> where P: FnMut(&Self::Item) -> bool94     fn peeking_take_while<P>(&mut self, predicate: P) -> PeekingTakeWhile<'_, I, P>
95     where
96         P: FnMut(&Self::Item) -> bool;
97 }
98 
99 impl<I: Iterator> PeekableExt<I> for core::iter::Peekable<I> {
100     #[inline]
peeking_take_while<P>(&mut self, predicate: P) -> PeekingTakeWhile<'_, I, P> where P: FnMut(&Self::Item) -> bool,101     fn peeking_take_while<P>(&mut self, predicate: P) -> PeekingTakeWhile<'_, I, P>
102     where
103         P: FnMut(&Self::Item) -> bool,
104     {
105         PeekingTakeWhile {
106             iter: self,
107             predicate,
108         }
109     }
110 }
111 
112 /// The iterator returned by `peeking_take_while`.
113 ///
114 /// See the [module documentation](./index.html) for details.
115 pub struct PeekingTakeWhile<'a, I, P>
116 where
117     I: Iterator,
118 {
119     pub(crate) iter: &'a mut core::iter::Peekable<I>,
120     pub(crate) predicate: P,
121 }
122 
123 impl<I, P> fmt::Debug for PeekingTakeWhile<'_, I, P>
124 where
125     I: Iterator + fmt::Debug,
126     I::Item: fmt::Debug,
127 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result128     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129         f.debug_struct("PeekingTakeWhile")
130             .field("iter", &self.iter)
131             .finish()
132     }
133 }
134 
135 impl<I, P> Iterator for PeekingTakeWhile<'_, I, P>
136 where
137     I: Iterator,
138     P: FnMut(&I::Item) -> bool,
139 {
140     type Item = I::Item;
141 
142     #[inline]
next(&mut self) -> Option<Self::Item>143     fn next(&mut self) -> Option<Self::Item> {
144         self.iter.next_if(&mut self.predicate)
145     }
146 
147     #[inline]
size_hint(&self) -> (usize, Option<usize>)148     fn size_hint(&self) -> (usize, Option<usize>) {
149         // can't know a lower bound, due to the predicate
150         (0, self.iter.size_hint().1)
151     }
152 
153     #[inline]
fold<B, F>(mut self, mut accum: B, mut f: F) -> B where F: FnMut(B, I::Item) -> B,154     fn fold<B, F>(mut self, mut accum: B, mut f: F) -> B
155     where
156         F: FnMut(B, I::Item) -> B,
157     {
158         while let Some(x) = self.iter.next_if(&mut self.predicate) {
159             accum = f(accum, x);
160         }
161         accum
162     }
163 }
164 
165 // interestingly, `PeekingTakeWhile` is not automatically fused,
166 // even when the inner iterator is fused, see also: `tests::not_fused`.
167 
168 #[cfg(test)]
169 mod tests {
170     use crate::PeekableExt;
171 
172     #[test]
basic()173     fn basic() {
174         let mut it0 = (1..11).peekable();
175         let a: u32 = it0.peeking_take_while(|&i| i < 5).sum();
176         let b: u32 = it0.sum();
177         assert_eq!(a, 10);
178         assert_eq!(b, 45);
179     }
180 
181     #[test]
basic_fused()182     fn basic_fused() {
183         let mut it0 = (1..11).peekable();
184         let a: u32 = it0.peeking_take_while(|&i| i < 5).fuse().sum();
185         let b: u32 = it0.sum();
186         assert_eq!(a, 10);
187         assert_eq!(b, 45);
188     }
189 
190     #[test]
not_fused()191     fn not_fused() {
192         let mut it0 = (0..10).peekable();
193         let mut ax = true;
194         let mut it1 = it0.peeking_take_while(|_| {
195             ax = !ax;
196             ax
197         });
198         assert!(it1.next().is_none());
199         assert_eq!(it1.next(), Some(0));
200         assert!(it1.next().is_none());
201         assert_eq!(it1.next(), Some(1));
202         assert_eq!(ax, true);
203     }
204 
205     #[test]
fused()206     fn fused() {
207         let mut it0 = (0..10).peekable();
208         let mut ax = true;
209         let mut it1 = it0
210             .peeking_take_while(|_| {
211                 ax = !ax;
212                 ax
213             })
214             .fuse();
215         assert!(it1.next().is_none());
216         assert!(it1.next().is_none());
217         assert_eq!(ax, false);
218     }
219 }
220