Add a try_reduce method to the Iterator trait

This commit is contained in:
kit 2021-07-11 15:43:30 +10:00
parent ff2439b7b9
commit aef59e4fb8
3 changed files with 109 additions and 0 deletions

View File

@ -2216,6 +2216,86 @@ pub trait Iterator {
Some(self.fold(first, f)) Some(self.fold(first, f))
} }
/// Reduces the elements to a single one by repeatedly applying a reducing operation. If the
/// closure returns a failure, the failure is propagated back to the caller immediately.
///
/// The return type of this method depends on the return type of the closure. If the closure
/// returns `Result<Self::Item, E>`, then this function will return `Result<Option<Self::Item>,
/// E>`. If the closure returns `Option<Self::Item>`, then this function will return
/// `Option<Option<Self::Item>>`.
///
/// When called on an empty iterator, this function will return either `Some(None)` or
/// `Ok(None)` depending on the type of the provided closure.
///
/// For iterators with at least one element, this is essentially the same as calling
/// [`try_fold()`] with the first element of the iterator as the initial accumulator value.
///
/// [`try_fold()`]: Iterator::try_fold
///
/// # Examples
///
/// Safely calculate the sum of a series of numbers:
///
/// ```
/// #![feature(iterator_try_reduce)]
///
/// let numbers: Vec<usize> = vec![10, 20, 5, 23, 0];
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
/// assert_eq!(sum, Some(Some(58)));
/// ```
///
/// Determine when a reduction short circuited:
///
/// ```
/// #![feature(iterator_try_reduce)]
///
/// let numbers = vec![1, 2, 3, usize::MAX, 4, 5];
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
/// assert_eq!(sum, None);
/// ```
///
/// Determine when a reduction was not performed because there are no elements:
///
/// ```
/// #![feature(iterator_try_reduce)]
///
/// let numbers: Vec<usize> = Vec::new();
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
/// assert_eq!(sum, Some(None));
/// ```
///
/// Use a [`Result`] instead of an [`Option`]:
///
/// ```
/// #![feature(iterator_try_reduce)]
///
/// let numbers = vec!["1", "2", "3", "4", "5"];
/// let max: Result<Option<_>, <usize as std::str::FromStr>::Err> =
/// numbers.into_iter().try_reduce(|x, y| {
/// if x.parse::<usize>()? > y.parse::<usize>()? { Ok(x) } else { Ok(y) }
/// });
/// assert_eq!(max, Ok(Some("5")));
/// ```
#[inline]
#[unstable(feature = "iterator_try_reduce", reason = "new API", issue = "87053")]
fn try_reduce<F, R>(&mut self, f: F) -> ChangeOutputType<R, Option<R::Output>>
where
Self: Sized,
F: FnMut(Self::Item, Self::Item) -> R,
R: Try<Output = Self::Item>,
R::Residual: Residual<Option<Self::Item>>,
{
let first = match self.next() {
Some(i) => i,
None => return Try::from_output(None),
};
match self.try_fold(first, f).branch() {
ControlFlow::Break(r) => FromResidual::from_residual(r),
ControlFlow::Continue(i) => Try::from_output(Some(i)),
}
}
/// Tests if every element of the iterator matches a predicate. /// Tests if every element of the iterator matches a predicate.
/// ///
/// `all()` takes a closure that returns `true` or `false`. It applies /// `all()` takes a closure that returns `true` or `false`. It applies

View File

@ -454,6 +454,34 @@ fn test_find_map() {
} }
} }
#[test]
fn test_try_reduce() {
let v: Vec<usize> = vec![1, 2, 3, 4, 5];
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
assert_eq!(sum, Some(Some(15)));
let v: Vec<usize> = vec![1, 2, 3, 4, 5, usize::MAX];
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
assert_eq!(sum, None);
let v: Vec<usize> = Vec::new();
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
assert_eq!(sum, Some(None));
let v = vec!["1", "2", "3", "4", "5"];
let max = v.into_iter().try_reduce(|x, y| {
if x.parse::<usize>().ok()? > y.parse::<usize>().ok()? { Some(x) } else { Some(y) }
});
assert_eq!(max, Some(Some("5")));
let v = vec!["1", "2", "3", "4", "5"];
let max: Result<Option<_>, <usize as std::str::FromStr>::Err> =
v.into_iter().try_reduce(|x, y| {
if x.parse::<usize>()? > y.parse::<usize>()? { Ok(x) } else { Ok(y) }
});
assert_eq!(max, Ok(Some("5")));
}
#[test] #[test]
fn test_iterator_len() { fn test_iterator_len() {
let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

View File

@ -56,6 +56,7 @@
#![feature(iter_intersperse)] #![feature(iter_intersperse)]
#![feature(iter_is_partitioned)] #![feature(iter_is_partitioned)]
#![feature(iter_order_by)] #![feature(iter_order_by)]
#![feature(iterator_try_reduce)]
#![feature(const_mut_refs)] #![feature(const_mut_refs)]
#![feature(const_pin)] #![feature(const_pin)]
#![feature(const_slice_from_raw_parts)] #![feature(const_slice_from_raw_parts)]