diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index bdf94c792c27c..dbaea89d35e78 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -301,6 +301,71 @@ pub trait DoubleEndedIterator: Iterator { accum } + /// Folds every element into an accumulator by applying an operation, + /// returning the final result, starting from the back. + /// The initial value is derived from the last element using the provided method. + /// + /// This is the reverse version of [`Iterator::fold_first()`]: it takes elements + /// starting from the back of the iterator. + /// + /// If the iterator is empty, returns [`None`]; otherwise, returns the + /// result of the fold. + /// + /// The folding function is a closure with two arguments: an 'accumulator', and an element. + /// + /// # Example + /// + /// ``` + /// #![feature(iterator_rfold_last)] + /// + /// let numbers = [1, 2, 3, 4, 5]; + /// + /// let result = numbers.iter().rfold_last( + /// |last| last.to_string(), + /// |acc, &x| format!("({x} + {acc})"), + /// ).unwrap(); + /// + /// assert_eq!(result, "(1 + (2 + (3 + (4 + 5))))"); + /// ``` + #[inline] + #[unstable(feature = "iterator_rfold_last", reason = "new API", issue = "none")] + fn rfold_last(mut self, init: F1, folding: FR) -> Option + where + Self: Sized, + F1: FnOnce(Self::Item) -> B, + FR: FnMut(B, Self::Item) -> B, + { + let last = init(self.next_back()?); + Some(self.rfold(last, folding)) + } + + /// This is the reverse version of [`Iterator::try_fold_first()`]: it takes + /// elements starting from the back of the iterator. + #[inline] + #[unstable(feature = "iterator_try_rfold_last", reason = "new API", issue = "none")] + fn try_rfold_last( + &mut self, + init: F1, + folding: FR, + ) -> ChangeOutputType> + where + Self: Sized, + F1: FnOnce(Self::Item) -> R, + FR: FnMut(R::Output, Self::Item) -> R, + R: Try, + R::Residual: Residual>, + { + let last = match self.next_back() { + Some(i) => init(i)?, + None => return Try::from_output(None), + }; + + match self.try_rfold(last, folding).branch() { + ControlFlow::Break(r) => FromResidual::from_residual(r), + ControlFlow::Continue(i) => Try::from_output(Some(i)), + } + } + /// Searches for an element of an iterator from the back that satisfies a predicate. /// /// `rfind()` takes a closure that returns `true` or `false`. It applies diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index fc4d4bff24f33..2d82224a6d71b 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -2448,13 +2448,12 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_fold_self", since = "1.51.0")] - fn reduce(mut self, f: F) -> Option + fn reduce(self, f: F) -> Option where Self: Sized, F: FnMut(Self::Item, Self::Item) -> Self::Item, { - let first = self.next()?; - Some(self.fold(first, f)) + self.fold_first(core::convert::identity, f) } /// Reduces the elements to a single one by repeatedly applying a reducing operation. If the @@ -2525,13 +2524,128 @@ pub trait Iterator { F: FnMut(Self::Item, Self::Item) -> R, R: Try, R::Residual: Residual>, + { + self.try_fold_first(Try::from_output, f) + } + + /// Folds every element into an accumulator by applying an operation, + /// returning the final result. The initial value is derived from the + /// first element using the provided method. + /// + /// If the iterator is empty, returns [`None`]; otherwise, returns the + /// result of the fold. + /// + /// The folding function is a closure with two arguments: an 'accumulator', and an element. + /// For iterators with at least one element, this is the same as [`reduce()`] + /// with the first element being fed into the init function + /// + /// [`reduce()`]: Iterator::reduce + /// + /// # Example + /// + /// ``` + /// #![feature(iterator_fold_first)] + /// + /// let min_max: (i32, i32) = [3, 1, 4, 1, 5, 9, 2] + /// .into_iter() + /// .fold_first( + /// |first| (first, first), + /// |(min, max), next| (i32::min(min, next), i32::max(max, next)), + /// ).unwrap(); + /// assert_eq!(min_max, (1, 9)); + /// + /// // Which is equivalent to doing it with `fold`: + /// let folded: (i32, i32) = [3, 1, 4, 1, 5, 9, 2] + /// .into_iter() + /// .fold( + /// (i32::MAX, i32::MIN), + /// |(min, max), next| (i32::min(min, next), i32::max(max, next)), + /// ); + /// assert_eq!(min_max, folded); + /// ``` + #[inline] + #[unstable(feature = "iterator_fold_first", reason = "new API", issue = "none")] + fn fold_first(mut self, init: F1, folding: FR) -> Option + where + Self: Sized, + F1: FnOnce(Self::Item) -> B, + FR: FnMut(B, Self::Item) -> B, + { + let first = init(self.next()?); + Some(self.fold(first, folding)) + } + + /// Folds every element into an accumulator by applying an operation, + /// returning the final result. The initial value is derived from the + /// first element using the provided method. + /// + /// If the closure returns a failure, the failure is propagated back to the caller immediately. + /// + /// # Example + /// + /// Replaying a series of events from creation + /// + /// ``` + /// #![feature(iterator_try_fold_first)] + /// + /// enum Events { + /// Create, + /// Update, + /// } + /// + /// let events = [Events::Create, Events::Update, Events::Update]; + /// let replayed_state = events.into_iter() + /// .try_fold_first( + /// |first| match first { + /// Events::Create => Ok(1), + /// _ => Err("only creation event supported at start"), + /// }, + /// |state, next| match next { + /// Events::Update => Ok(state + 1), + /// _ => Err("only update events should follow a creation"), + /// }, + /// ); + /// assert_eq!(replayed_state, Ok(Some(3))); + /// + /// // Which is equivalent to doing it with `try_fold`: + /// let events = [Events::Create, Events::Update, Events::Update]; + /// let folded = events.into_iter() + /// .try_fold( + /// None, + /// |state, event| { + /// match (state, event) { + /// // init + /// (None, Events::Create) => Ok(Some(1)), + /// (None, Events::Update) => Err("only update events should follow a creation"), + /// + /// // fold + /// (Some(state), Events::Update) => Ok(Some(state + 1)), + /// (Some(_), Events::Create) => Err("only creation event supported at start"), + /// } + /// }, + /// ); + /// assert_eq!(replayed_state, folded); + /// ``` + #[inline] + #[unstable(feature = "iterator_try_fold_first", reason = "new API", issue = "none")] + fn try_fold_first( + &mut self, + init: F1, + folding: FR, + ) -> ChangeOutputType> + where + Self: Sized, + F1: FnOnce(Self::Item) -> R, + FR: FnMut(R::Output, Self::Item) -> R, + R: Try, + R::Residual: Residual>, { let first = match self.next() { - Some(i) => i, + Some(i) => init(i)?, None => return Try::from_output(None), }; - match self.try_fold(first, f).branch() { + match self.try_fold(first, folding).branch() { ControlFlow::Break(r) => FromResidual::from_residual(r), ControlFlow::Continue(i) => Try::from_output(Some(i)), }