diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 19484bfd0419f..8cda5d731aa90 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -2231,6 +2231,179 @@ pub trait Iterator { self.try_fold((), check(predicate)).break_value() } + /// Searches for an element of an iterator that satisfies a predicate, returns the first + /// element if no such element is found. + /// + /// `find_or_first()` takes a closure that returns `true` or `false`. It applies this closure + /// to each element of the iterator, and if any of them return `true`, then `find_or_first()` + /// returns [`Some(element)`]. If they all return `false`, it returns the same as if [`next`] + /// was invoked instead, though the iterator will be empty. + /// + /// `find_or_first()` is short-circuiting; in other words, it will stop processing as soon as + /// closure returns `true`. + /// + /// Because `find_or_first()` takes a reference, and many iterators iterate over references, + /// this leads to a possibly confusing situation where the argument is a double reference. You + /// can see this effect in the examples below, with `&&x`. + /// + /// [`Some(element)`]: Some + /// [`next`]: Iterator::next + /// + /// # Example + /// + /// ``` + /// #![feature(iter_find_or_fnl)] + /// + /// let a = [0, 1, 2, 3]; + /// + /// assert_eq!( + /// a.iter().find_or_first(|&&x| x > 1), + /// Some(&2) // predicate is satisfied + /// ); + /// assert_eq!( + /// a.iter().find_or_first(|&&x| x > 10), + /// Some(&0) // predicate not satisfied, first element is returned + /// ); + /// ``` + #[inline] + #[unstable(feature = "iter_find_or_fnl", reason = "recently added", issue = "none")] + fn find_or_first

(&mut self, mut predicate: P) -> Option + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + match self.next() { + Some(item) if predicate(&item) => Some(item), + default => self.find(predicate).or(default), + } + } + + /// Searches for an element of an iterator that satisfies a predicate, returns the `n`th + /// element if no such element is found. + /// + /// `find_or_nth()` takes a closure that returns `true` or `false`. It applies this closure to + /// each element of the iterator, and if any of them return `true`, then `find_or_nth()` + /// returns [`Some(element)`]. If they all return `false`, it returns the same as if [`nth(n)`] + /// was invoked instead, though the iterator will be empty. + /// + /// Like most indexing operations, the count starts from zero, so if `predicate` is never + /// satisfied, `find_or_nth(predicate, 0)` returns the first item, `find_or_nth(1)` the second, + /// and so on. + /// + /// `find_or_nth()` is short-circuiting; in other words, it will stop processing as soon as the + /// closure returns `true`. + /// + /// Because `find_or_nth()` takes a reference, and many iterators iterate over references, this + /// leads to a possibly confusing situation where the argument is a double reference. You can + /// see this effect in the examples below, with `&&x`. + /// + /// [`Some(element)`]: Some + /// [`nth(n)`]: Iterator::nth + /// + /// # Example + /// + /// ``` + /// #![feature(iter_find_or_fnl)] + /// + /// let a = [0, 1, 2, 3]; + /// + /// assert_eq!( + /// a.iter().find_or_nth(|&&x| x > 1, 2), + /// Some(&2) // predicate is satisfied + /// ); + /// assert_eq!( + /// a.iter().find_or_nth(|&&x| x > 10, 2), + /// Some(&2) // predicate not satisfied, 3rd element is returned + /// ); + /// assert_eq!( + /// a.iter().find_or_nth(|&&x| x > 10, 5), + /// None // predicate not satisfied, no 6th element + /// ); + /// ``` + #[inline] + #[unstable(feature = "iter_find_or_fnl", reason = "recently added", issue = "none")] + fn find_or_nth

(&mut self, predicate: P, n: usize) -> Option + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + #[inline] + fn check( + mut predicate: impl FnMut(&T) -> bool, + n: usize, + ) -> impl FnMut(Option, (usize, T)) -> ControlFlow> { + move |prev, (idx, cur)| { + if predicate(&cur) { + ControlFlow::Break(cur) + } else if idx == n { + debug_assert!(prev.is_none()); + ControlFlow::Continue(Some(cur)) + } else { + debug_assert_eq!(idx < n, prev.is_none()); + ControlFlow::Continue(prev) + } + } + } + + self.enumerate().try_fold(None, check(predicate, n)).into_result().unwrap_or_else(Some) + } + + /// Searches for an element of an iterator that satisfies a predicate, returns the last + /// element if no such element is found. + /// + /// `find_or_last()` takes a closure that returns `true` or `false`. It applies this closure to + /// each element of the iterator, and if any of them return `true`, then `find_or_last()` + /// returns [`Some(element)`]. If they all return `false`, it returns the same as if [`last`] + /// was invoked instead. + /// + /// `find_or_last()` is short-circuiting; in other words, it will stop processing as soon as the + /// closure returns `true`. + /// + /// Because `find_or_last()` takes a reference, and many iterators iterate over references, this + /// leads to a possibly confusing situation where the argument is a double reference. You can + /// see this effect in the examples below, with `&&x`. + /// + /// [`Some(element)`]: Some + /// [`last`]: Iterator::last + /// + /// # Example + /// + /// ``` + /// #![feature(iter_find_or_fnl)] + /// + /// let a = [0, 1, 2, 3]; + /// + /// assert_eq!( + /// a.iter().find_or_last(|&&x| x > 1), + /// Some(&2) // predicate is satisfied + /// ); + /// assert_eq!( + /// a.iter().find_or_last(|&&x| x > 10), + /// Some(&3) // predicate not satisfied, last element is returned + /// ); + /// ``` + #[inline] + #[unstable(feature = "iter_find_or_fnl", reason = "recently added", issue = "none")] + fn find_or_last

(&mut self, predicate: P) -> Option + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + #[inline] + fn check( + mut predicate: impl FnMut(&T) -> bool, + ) -> impl FnMut(Option, T) -> ControlFlow> { + move |_, cur| { + if predicate(&cur) { + ControlFlow::Break(cur) + } else { + ControlFlow::Continue(Some(cur)) + } + } + } + self.try_fold(None, check(predicate)).into_result().unwrap_or_else(Some) + } + /// Applies function to the elements of iterator and returns /// the first non-none result. /// diff --git a/library/core/tests/iter.rs b/library/core/tests/iter.rs index 75ca897cadc91..013989b5d32dd 100644 --- a/library/core/tests/iter.rs +++ b/library/core/tests/iter.rs @@ -1930,6 +1930,62 @@ fn test_find() { assert!(v.iter().find(|&&x| x % 12 == 0).is_none()); } +#[test] +fn find_or_first() { + assert_eq!([0, 1, 2, 3].iter().find_or_first(|&&n| n > 1), Some(&2)); + assert_eq!([0, 1, 2, 3].iter().find_or_first(|&&n| n > 2), Some(&3)); + assert_eq!([0, 1, 2, 3].iter().find_or_first(|&&n| n > 3), Some(&0)); + assert_eq!([3, 2, 1, 0].iter().find_or_first(|&&n| n > 1), Some(&3)); + assert_eq!([1].iter().find_or_first(|&&n| n > 1), Some(&1)); + assert_eq!([2].iter().find_or_first(|&&n| n > 1), Some(&2)); + assert_eq!([(); 0].iter().find_or_first(|()| unreachable!()), None); +} + +#[test] +fn find_or_nth() { + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 1, 0), Some(&2)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 1, 1), Some(&2)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 1, 2), Some(&2)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 1, 3), Some(&2)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 1, 4), Some(&2)); + + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 2, 0), Some(&3)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 2, 1), Some(&3)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 2, 2), Some(&3)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 2, 3), Some(&3)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 2, 4), Some(&3)); + + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 3, 0), Some(&0)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 3, 1), Some(&1)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 3, 2), Some(&2)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 3, 3), Some(&3)); + assert_eq!([0, 1, 2, 3].iter().find_or_nth(|&&n| n > 3, 4), None); + + assert_eq!([3, 2, 1, 0].iter().find_or_nth(|&&n| n > 1, 0), Some(&3)); + assert_eq!([3, 2, 1, 0].iter().find_or_nth(|&&n| n > 1, 1), Some(&3)); + assert_eq!([3, 2, 1, 0].iter().find_or_nth(|&&n| n > 1, 2), Some(&3)); + assert_eq!([3, 2, 1, 0].iter().find_or_nth(|&&n| n > 1, 3), Some(&3)); + assert_eq!([3, 2, 1, 0].iter().find_or_nth(|&&n| n > 1, 4), Some(&3)); + + assert_eq!([1].iter().find_or_nth(|&&n| n > 1, 0), Some(&1)); + assert_eq!([1].iter().find_or_nth(|&&n| n > 1, 1), None); + assert_eq!([2].iter().find_or_nth(|&&n| n > 1, 0), Some(&2)); + assert_eq!([2].iter().find_or_nth(|&&n| n > 1, 1), Some(&2)); + + assert_eq!([(); 0].iter().find_or_nth(|()| unreachable!(), 0), None); +} + +#[test] +fn find_or_last() { + assert_eq!([0, 1, 2, 3].iter().find_or_last(|&&n| n > 1), Some(&2)); + assert_eq!([0, 1, 2, 3].iter().find_or_last(|&&n| n > 2), Some(&3)); + assert_eq!([0, 1, 2, 3].iter().find_or_last(|&&n| n > 3), Some(&3)); + assert_eq!([3, 2, 1, 0].iter().find_or_last(|&&n| n > 1), Some(&3)); + assert_eq!([1].iter().find_or_last(|&&n| n > 1), Some(&1)); + assert_eq!([2].iter().find_or_last(|&&n| n > 1), Some(&2)); + assert_eq!([(); 0].iter().find_or_last(|()| unreachable!()), None); +} + #[test] fn test_find_map() { let xs: &[isize] = &[]; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 14ef03fd53eba..fabf3c36f3417 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -43,6 +43,7 @@ #![feature(int_error_matching)] #![feature(array_value_iter)] #![feature(iter_advance_by)] +#![feature(iter_find_or_fnl)] #![feature(iter_partition_in_place)] #![feature(iter_is_partitioned)] #![feature(iter_order_by)]