224 lines
6.6 KiB
Rust
224 lines
6.6 KiB
Rust
//! Provides the `peeking_take_while` iterator adaptor method.
|
|
//!
|
|
//! The `peeking_take_while` method is very similar to `take_while`, but behaves
|
|
//! differently when used with a borrowed iterator (perhaps returned by
|
|
//! `Iterator::by_ref`).
|
|
//!
|
|
//! `peeking_take_while` peeks at the next item in the iterator and runs the
|
|
//! predicate on that peeked item. This avoids consuming the first item yielded
|
|
//! by the underlying iterator for which the predicate returns `false`. On the
|
|
//! other hand, `take_while` will consume that first item for which the
|
|
//! predicate returns `false`, and it will be lost.
|
|
//!
|
|
//! In case the closure may have side effects, it could be necessary to apply
|
|
//! [`fuse`](Iterator::fuse) on the returned iterator, to prevent the predicate
|
|
//! from being called after it first returned `false`.
|
|
//!
|
|
//! ```
|
|
//! // Bring the `peeking_take_while` method for peekable iterators into
|
|
//! // scope.
|
|
//! use peeking_take_while::PeekableExt;
|
|
//!
|
|
//! # fn main() {
|
|
//! // Let's say we have two collections we want to iterate through: `xs` and
|
|
//! // `ys`. We want to perform one operation on all the leading contiguous
|
|
//! // elements that match some predicate, and a different thing with the rest of
|
|
//! // the elements. With the `xs`, we will use the normal `take_while`. With the
|
|
//! // `ys`, we will use `peeking_take_while`.
|
|
//!
|
|
//! let xs: Vec<u8> = (0..100).collect();
|
|
//! let ys = xs.clone();
|
|
//!
|
|
//! let mut iter_xs = xs.into_iter();
|
|
//! let mut iter_ys = ys.into_iter().peekable();
|
|
//!
|
|
//! {
|
|
//! // Let's do one thing with all the items that are less than 10.
|
|
//! # fn do_things_with<T>(_: T) {}
|
|
//!
|
|
//! let xs_less_than_ten = iter_xs.by_ref().take_while(|x| *x < 10);
|
|
//! for x in xs_less_than_ten {
|
|
//! do_things_with(x);
|
|
//! }
|
|
//!
|
|
//! let ys_less_than_ten = iter_ys.by_ref().peeking_take_while(|y| *y < 10);
|
|
//! for y in ys_less_than_ten {
|
|
//! do_things_with(y);
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! // And now we will do some other thing with the items that are greater than
|
|
//! // or equal to 10.
|
|
//!
|
|
//! // ...except, when using plain old `take_while` we lost 10!
|
|
//! assert_eq!(iter_xs.next(), Some(11));
|
|
//!
|
|
//! // However, when using `peeking_take_while` we did not! Great!
|
|
//! assert_eq!(iter_ys.next(), Some(10));
|
|
//! # }
|
|
//! ```
|
|
|
|
#![no_std]
|
|
#![forbid(
|
|
clippy::as_conversions,
|
|
clippy::cast_ptr_alignment,
|
|
missing_docs,
|
|
trivial_casts,
|
|
unsafe_code
|
|
)]
|
|
|
|
// ANDROID: Unconditionally use std to allow building as a dylib
|
|
#[macro_use]
|
|
extern crate std;
|
|
|
|
use core::fmt;
|
|
|
|
/// The `Iterator` extension trait that provides the `peeking_take_while`
|
|
/// method.
|
|
///
|
|
/// See the [module documentation](./index.html) for details.
|
|
pub trait PeekableExt<I>: Iterator
|
|
where
|
|
I: Iterator,
|
|
{
|
|
/// The `peeking_take_while` method is very similar to `take_while`, but behaves
|
|
/// differently when used with a borrowed iterator (perhaps returned by
|
|
/// `Iterator::by_ref`).
|
|
///
|
|
/// `peeking_take_while` peeks at the next item in the iterator and runs the
|
|
/// predicate on that peeked item. This avoids consuming the first item yielded
|
|
/// by the underlying iterator for which the predicate returns `false`. On the
|
|
/// other hand, `take_while` will consume that first item for which the
|
|
/// predicate returns `false`, and it will be lost.
|
|
///
|
|
/// In contrast to `take_while`, iterating the iterator might call the predicate again
|
|
/// after it first returned `false` (the returned iterator isn't fused).
|
|
/// If that is not intended, calling [`fuse`](Iterator::fuse) on the returned iterator
|
|
/// prevents that.
|
|
fn peeking_take_while<P>(&mut self, predicate: P) -> PeekingTakeWhile<'_, I, P>
|
|
where
|
|
P: FnMut(&Self::Item) -> bool;
|
|
}
|
|
|
|
impl<I: Iterator> PeekableExt<I> for core::iter::Peekable<I> {
|
|
#[inline]
|
|
fn peeking_take_while<P>(&mut self, predicate: P) -> PeekingTakeWhile<'_, I, P>
|
|
where
|
|
P: FnMut(&Self::Item) -> bool,
|
|
{
|
|
PeekingTakeWhile {
|
|
iter: self,
|
|
predicate,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The iterator returned by `peeking_take_while`.
|
|
///
|
|
/// See the [module documentation](./index.html) for details.
|
|
pub struct PeekingTakeWhile<'a, I, P>
|
|
where
|
|
I: Iterator,
|
|
{
|
|
pub(crate) iter: &'a mut core::iter::Peekable<I>,
|
|
pub(crate) predicate: P,
|
|
}
|
|
|
|
impl<I, P> fmt::Debug for PeekingTakeWhile<'_, I, P>
|
|
where
|
|
I: Iterator + fmt::Debug,
|
|
I::Item: fmt::Debug,
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("PeekingTakeWhile")
|
|
.field("iter", &self.iter)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl<I, P> Iterator for PeekingTakeWhile<'_, I, P>
|
|
where
|
|
I: Iterator,
|
|
P: FnMut(&I::Item) -> bool,
|
|
{
|
|
type Item = I::Item;
|
|
|
|
#[inline]
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
self.iter.next_if(&mut self.predicate)
|
|
}
|
|
|
|
#[inline]
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
// can't know a lower bound, due to the predicate
|
|
(0, self.iter.size_hint().1)
|
|
}
|
|
|
|
#[inline]
|
|
fn fold<B, F>(mut self, mut accum: B, mut f: F) -> B
|
|
where
|
|
F: FnMut(B, I::Item) -> B,
|
|
{
|
|
while let Some(x) = self.iter.next_if(&mut self.predicate) {
|
|
accum = f(accum, x);
|
|
}
|
|
accum
|
|
}
|
|
}
|
|
|
|
// interestingly, `PeekingTakeWhile` is not automatically fused,
|
|
// even when the inner iterator is fused, see also: `tests::not_fused`.
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::PeekableExt;
|
|
|
|
#[test]
|
|
fn basic() {
|
|
let mut it0 = (1..11).peekable();
|
|
let a: u32 = it0.peeking_take_while(|&i| i < 5).sum();
|
|
let b: u32 = it0.sum();
|
|
assert_eq!(a, 10);
|
|
assert_eq!(b, 45);
|
|
}
|
|
|
|
#[test]
|
|
fn basic_fused() {
|
|
let mut it0 = (1..11).peekable();
|
|
let a: u32 = it0.peeking_take_while(|&i| i < 5).fuse().sum();
|
|
let b: u32 = it0.sum();
|
|
assert_eq!(a, 10);
|
|
assert_eq!(b, 45);
|
|
}
|
|
|
|
#[test]
|
|
fn not_fused() {
|
|
let mut it0 = (0..10).peekable();
|
|
let mut ax = true;
|
|
let mut it1 = it0.peeking_take_while(|_| {
|
|
ax = !ax;
|
|
ax
|
|
});
|
|
assert!(it1.next().is_none());
|
|
assert_eq!(it1.next(), Some(0));
|
|
assert!(it1.next().is_none());
|
|
assert_eq!(it1.next(), Some(1));
|
|
assert_eq!(ax, true);
|
|
}
|
|
|
|
#[test]
|
|
fn fused() {
|
|
let mut it0 = (0..10).peekable();
|
|
let mut ax = true;
|
|
let mut it1 = it0
|
|
.peeking_take_while(|_| {
|
|
ax = !ax;
|
|
ax
|
|
})
|
|
.fuse();
|
|
assert!(it1.next().is_none());
|
|
assert!(it1.next().is_none());
|
|
assert_eq!(ax, false);
|
|
}
|
|
}
|