peeking_take_while/lib.rs
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
70use 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.
76pub trait PeekableExt<I>: Iterator
77where
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.
94 fn peeking_take_while<P>(&mut self, predicate: P) -> PeekingTakeWhile<'_, I, P>
95 where
96 P: FnMut(&Self::Item) -> bool;
97}
98
99impl<I: Iterator> PeekableExt<I> for core::iter::Peekable<I> {
100 #[inline]
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.
115pub struct PeekingTakeWhile<'a, I, P>
116where
117 I: Iterator,
118{
119 pub(crate) iter: &'a mut core::iter::Peekable<I>,
120 pub(crate) predicate: P,
121}
122
123impl<I, P> fmt::Debug for PeekingTakeWhile<'_, I, P>
124where
125 I: Iterator + fmt::Debug,
126 I::Item: fmt::Debug,
127{
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.debug_struct("PeekingTakeWhile")
130 .field("iter", &self.iter)
131 .finish()
132 }
133}
134
135impl<I, P> Iterator for PeekingTakeWhile<'_, I, P>
136where
137 I: Iterator,
138 P: FnMut(&I::Item) -> bool,
139{
140 type Item = I::Item;
141
142 #[inline]
143 fn next(&mut self) -> Option<Self::Item> {
144 self.iter.next_if(&mut self.predicate)
145 }
146
147 #[inline]
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]
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)]
169mod tests {
170 use crate::PeekableExt;
171
172 #[test]
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]
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]
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]
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}