diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 46e1a3a4aa2f..ce026d6965f4 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -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(&mut self, n: usize, f: F) -> bool + where + Self: Sized, + F: FnMut(Self::Item) -> bool, + { + #[inline] + fn check( + n: usize, + mut f: impl FnMut(T) -> bool, + ) -> impl FnMut(usize, T) -> ControlFlow { + 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(&mut self, n: usize, f: F) -> bool + where + Self: Sized, + F: FnMut(Self::Item) -> bool, + { + #[inline] + fn check( + n: usize, + mut f: impl FnMut(T) -> bool, + ) -> impl FnMut(usize, T) -> ControlFlow { + 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 diff --git a/library/core/tests/iter/traits/iterator.rs b/library/core/tests/iter/traits/iterator.rs index 422e389e3801..ffc5d17498cf 100644 --- a/library/core/tests/iter/traits/iterator.rs +++ b/library/core/tests/iter/traits/iterator.rs @@ -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]; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 997e618cc511..5b24d71d5bb7 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -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)]