Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Iterator::at_least() and Iterator::at_most() API #80835

Closed
wants to merge 9 commits into from
132 changes: 132 additions & 0 deletions library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2286,6 +2286,138 @@ pub trait Iterator {
self.try_fold((), check(f)) == ControlFlow::BREAK
}

/// Tests if at least `n` elements of the iterator matches a predicate.
///
/// `at_least()` takes a usize `n` and a closure that returns `true` or `false`. It applies
/// this closure to each element of the iterator, and if more than `n` of them return
/// `true`, then so does `at_least()`. If less than `n` of them return `true`, it
/// returns `false`.
///
/// `at_least()` is short-circuiting; in other words, it will stop processing
/// as soon as it finds `n` `true`, given that no matter what else happens,
/// the result will also be `true`.
///
/// An empty iterator returns `false`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(iter_at_least)]
///
/// let a = [1, 2, 3];
///
/// assert!(a.iter().at_least(1, |&x| x > 0));
/// assert!(!a.iter().at_least(1, |&x| x > 5));
/// ```
///
/// Stopping at the `n`th `true`:
///
/// ```
/// #![feature(iter_at_least)]
///
/// let a = vec![1, 2, 3, 4, 5];
///
/// assert!(a.iter().at_least(0, |&x| x % 2 == 0));
/// assert!(a.iter().at_least(1, |&x| x % 2 == 0));
/// assert!(a.iter().at_least(2, |&x| x % 2 == 0));
/// assert!(!a.iter().at_least(3, |&x| x % 2 == 0));
///
/// // we can still use `iter`, as there are more elements.
/// let a = [1, 2, 3];
/// let mut iter = a.iter();
/// assert!(iter.at_least(1, |&x| x > 0));
/// assert_eq!(iter.next(), Some(&2));
/// ```
#[inline]
#[unstable(feature = "iter_at_least", reason = "new API", issue = "none")]
fn at_least<F>(&mut self, n: usize, f: F) -> bool
where
Self: Sized,
F: FnMut(Self::Item) -> bool,
{
#[inline]
fn check<T>(
n: usize,
mut f: impl FnMut(T) -> bool,
) -> impl FnMut(usize, T) -> ControlFlow<usize, usize> {
move |mut i, x| {
i += f(x) as usize;

if i < n { ControlFlow::Continue(i) } else { ControlFlow::Break(i) }
}
}

matches!(self.try_fold(0, check(n, f)), ControlFlow::Break(_))
}

/// Tests if at most `n` elements of the iterator matches a predicate.
///
/// `at_most()` takes a usize `n` and a closure that returns `true` or `false`. It applies
/// this closure to each element of the iterator, and if less than `n` of them return
/// `true`, then so does `at_least()`. If more than `n` of them return `true`, it
/// returns `false`.
///
/// `at_most()` is short-circuiting; in other words, it will stop processing
/// as soon as it finds `n` `false`, given that no matter what else happens,
/// the result will also be `false`.
///
/// An empty iterator returns `true`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(iter_at_most)]
///
/// let a = [1, 2, 3];
///
/// assert!(a.iter().at_most(1, |&x| x > 3));
/// assert!(!a.iter().at_most(1, |&x| x > 0));
/// ```
///
/// Stopping at the `n + 1`th `true`:
///
/// ```
/// #![feature(iter_at_most)]
///
/// let a = vec![1, 2, 3, 4, 5];
///
/// assert!(!a.iter().at_most(0, |&x| x % 2 == 0));
/// assert!(!a.iter().at_most(1, |&x| x % 2 == 0));
/// assert!(a.iter().at_most(2, |&x| x % 2 == 0));
/// assert!(a.iter().at_most(3, |&x| x % 2 == 0));
///
/// // we can still use `iter`, as there are more elements.
/// let a = [1, 1, 3];
/// let mut iter = a.iter();
/// assert!(!iter.at_most(1, |&x| x == 1));
/// assert_eq!(iter.next(), Some(&3));
/// ```
#[inline]
#[unstable(feature = "iter_at_most", reason = "new API", issue = "none")]
fn at_most<F>(&mut self, n: usize, f: F) -> bool
where
Self: Sized,
F: FnMut(Self::Item) -> bool,
{
#[inline]
fn check<T>(
n: usize,
mut f: impl FnMut(T) -> bool,
) -> impl FnMut(usize, T) -> ControlFlow<usize, usize> {
move |mut i, x| {
i += f(x) as usize;

if i <= n { ControlFlow::Continue(i) } else { ControlFlow::Break(i) }
}
}

matches!(self.try_fold(0, check(n, f)), ControlFlow::Continue(_))
}

/// Searches for an element of an iterator that satisfies a predicate.
///
/// `find()` takes a closure that returns `true` or `false`. It applies
Expand Down
26 changes: 26 additions & 0 deletions library/core/tests/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,32 @@ fn test_any() {
assert!(!v[..0].iter().any(|_| panic!()));
}

#[test]
fn test_at_least() {
let vec = vec![1, 2, 3, 4, 5];
assert!(vec.iter().at_least(0, |i| i % 2 == 0));
assert!(vec.iter().at_least(1, |i| i % 2 == 0));
assert!(vec.iter().at_least(2, |i| i % 2 == 0));
assert!(!vec.iter().at_least(3, |i| i % 2 == 0));
assert!(vec.iter().at_least(0, |&i| i > 100));
assert!(vec.iter().at_least(5, |&i| i < 100));

assert!(!&vec[..0].iter().at_least(0, |_| panic!()));
}

#[test]
fn test_at_most() {
let vec = vec![1, 2, 3, 4, 5];
assert!(vec.iter().at_most(0, |&i| i > 100));
assert!(vec.iter().at_most(5, |&i| i < 100));
assert!(vec.iter().at_most(3, |i| i % 2 == 0));
assert!(vec.iter().at_most(2, |i| i % 2 == 0));
assert!(!vec.iter().at_most(1, |i| i % 2 == 0));
assert!(!vec.iter().at_most(0, |i| i % 2 == 0));

assert!(&vec[..0].iter().at_most(0, |_| panic!()));
}

#[test]
fn test_find() {
let v: &[isize] = &[1, 3, 9, 27, 103, 14, 11];
Expand Down
2 changes: 2 additions & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
#![feature(integer_atomics)]
#![feature(slice_group_by)]
#![feature(trusted_random_access)]
#![feature(iter_at_least)]
#![feature(iter_at_most)]
#![cfg_attr(bootstrap, feature(unsafe_block_in_unsafe_fn))]
#![cfg_attr(not(bootstrap), feature(unsize))]
#![deny(unsafe_op_in_unsafe_fn)]
Expand Down