Skip to content

Commit c223a1c

Browse files
authored
Rollup merge of #87054 - kit-981:master, r=scottmcm
Add a `try_reduce` method to the Iterator trait Tracking issue: #87053
2 parents 887999d + aef59e4 commit c223a1c

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

library/core/src/iter/traits/iterator.rs

+80
Original file line numberDiff line numberDiff line change
@@ -2216,6 +2216,86 @@ pub trait Iterator {
22162216
Some(self.fold(first, f))
22172217
}
22182218

2219+
/// Reduces the elements to a single one by repeatedly applying a reducing operation. If the
2220+
/// closure returns a failure, the failure is propagated back to the caller immediately.
2221+
///
2222+
/// The return type of this method depends on the return type of the closure. If the closure
2223+
/// returns `Result<Self::Item, E>`, then this function will return `Result<Option<Self::Item>,
2224+
/// E>`. If the closure returns `Option<Self::Item>`, then this function will return
2225+
/// `Option<Option<Self::Item>>`.
2226+
///
2227+
/// When called on an empty iterator, this function will return either `Some(None)` or
2228+
/// `Ok(None)` depending on the type of the provided closure.
2229+
///
2230+
/// For iterators with at least one element, this is essentially the same as calling
2231+
/// [`try_fold()`] with the first element of the iterator as the initial accumulator value.
2232+
///
2233+
/// [`try_fold()`]: Iterator::try_fold
2234+
///
2235+
/// # Examples
2236+
///
2237+
/// Safely calculate the sum of a series of numbers:
2238+
///
2239+
/// ```
2240+
/// #![feature(iterator_try_reduce)]
2241+
///
2242+
/// let numbers: Vec<usize> = vec![10, 20, 5, 23, 0];
2243+
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
2244+
/// assert_eq!(sum, Some(Some(58)));
2245+
/// ```
2246+
///
2247+
/// Determine when a reduction short circuited:
2248+
///
2249+
/// ```
2250+
/// #![feature(iterator_try_reduce)]
2251+
///
2252+
/// let numbers = vec![1, 2, 3, usize::MAX, 4, 5];
2253+
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
2254+
/// assert_eq!(sum, None);
2255+
/// ```
2256+
///
2257+
/// Determine when a reduction was not performed because there are no elements:
2258+
///
2259+
/// ```
2260+
/// #![feature(iterator_try_reduce)]
2261+
///
2262+
/// let numbers: Vec<usize> = Vec::new();
2263+
/// let sum = numbers.into_iter().try_reduce(|x, y| x.checked_add(y));
2264+
/// assert_eq!(sum, Some(None));
2265+
/// ```
2266+
///
2267+
/// Use a [`Result`] instead of an [`Option`]:
2268+
///
2269+
/// ```
2270+
/// #![feature(iterator_try_reduce)]
2271+
///
2272+
/// let numbers = vec!["1", "2", "3", "4", "5"];
2273+
/// let max: Result<Option<_>, <usize as std::str::FromStr>::Err> =
2274+
/// numbers.into_iter().try_reduce(|x, y| {
2275+
/// if x.parse::<usize>()? > y.parse::<usize>()? { Ok(x) } else { Ok(y) }
2276+
/// });
2277+
/// assert_eq!(max, Ok(Some("5")));
2278+
/// ```
2279+
#[inline]
2280+
#[unstable(feature = "iterator_try_reduce", reason = "new API", issue = "87053")]
2281+
fn try_reduce<F, R>(&mut self, f: F) -> ChangeOutputType<R, Option<R::Output>>
2282+
where
2283+
Self: Sized,
2284+
F: FnMut(Self::Item, Self::Item) -> R,
2285+
R: Try<Output = Self::Item>,
2286+
R::Residual: Residual<Option<Self::Item>>,
2287+
{
2288+
let first = match self.next() {
2289+
Some(i) => i,
2290+
None => return Try::from_output(None),
2291+
};
2292+
2293+
match self.try_fold(first, f).branch() {
2294+
ControlFlow::Break(r) => FromResidual::from_residual(r),
2295+
ControlFlow::Continue(i) => Try::from_output(Some(i)),
2296+
}
2297+
}
2298+
22192299
/// Tests if every element of the iterator matches a predicate.
22202300
///
22212301
/// `all()` takes a closure that returns `true` or `false`. It applies

library/core/tests/iter/traits/iterator.rs

+28
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,34 @@ fn test_find_map() {
454454
}
455455
}
456456

457+
#[test]
458+
fn test_try_reduce() {
459+
let v: Vec<usize> = vec![1, 2, 3, 4, 5];
460+
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
461+
assert_eq!(sum, Some(Some(15)));
462+
463+
let v: Vec<usize> = vec![1, 2, 3, 4, 5, usize::MAX];
464+
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
465+
assert_eq!(sum, None);
466+
467+
let v: Vec<usize> = Vec::new();
468+
let sum = v.into_iter().try_reduce(|x, y| x.checked_add(y));
469+
assert_eq!(sum, Some(None));
470+
471+
let v = vec!["1", "2", "3", "4", "5"];
472+
let max = v.into_iter().try_reduce(|x, y| {
473+
if x.parse::<usize>().ok()? > y.parse::<usize>().ok()? { Some(x) } else { Some(y) }
474+
});
475+
assert_eq!(max, Some(Some("5")));
476+
477+
let v = vec!["1", "2", "3", "4", "5"];
478+
let max: Result<Option<_>, <usize as std::str::FromStr>::Err> =
479+
v.into_iter().try_reduce(|x, y| {
480+
if x.parse::<usize>()? > y.parse::<usize>()? { Ok(x) } else { Ok(y) }
481+
});
482+
assert_eq!(max, Ok(Some("5")));
483+
}
484+
457485
#[test]
458486
fn test_iterator_len() {
459487
let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#![feature(iter_intersperse)]
5757
#![feature(iter_is_partitioned)]
5858
#![feature(iter_order_by)]
59+
#![feature(iterator_try_reduce)]
5960
#![feature(const_mut_refs)]
6061
#![feature(const_pin)]
6162
#![feature(const_slice_from_raw_parts)]

0 commit comments

Comments
 (0)