From d3d163aaabd1b22ef1c237731603e5c774d6bf60 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sat, 31 Dec 2022 23:05:21 +0000 Subject: [PATCH 1/4] impl fold_first --- library/core/src/iter/traits/iterator.rs | 52 ++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index fc4d4bff24f33..c6f8d5de0ab7e 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 @@ -2537,6 +2536,53 @@ pub trait Iterator { } } + /// 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)) + } + /// Tests if every element of the iterator matches a predicate. /// /// `all()` takes a closure that returns `true` or `false`. It applies From fa73016a235fbe2fd08a2ef1b112dedd6eda2734 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sat, 31 Dec 2022 23:21:54 +0000 Subject: [PATCH 2/4] try_fold_first --- library/core/src/iter/traits/iterator.rs | 86 +++++++++++++++++++++--- 1 file changed, 77 insertions(+), 9 deletions(-) diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index c6f8d5de0ab7e..2d82224a6d71b 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -2525,15 +2525,7 @@ pub trait Iterator { R: Try, R::Residual: Residual>, { - 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)), - } + self.try_fold_first(Try::from_output, f) } /// Folds every element into an accumulator by applying an operation, @@ -2583,6 +2575,82 @@ pub trait Iterator { 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) => init(i)?, + None => return Try::from_output(None), + }; + + match self.try_fold(first, folding).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. /// /// `all()` takes a closure that returns `true` or `false`. It applies From 249db11d5f92851388b6809310043f99edc71b2c Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 1 Jan 2023 11:58:05 +0000 Subject: [PATCH 3/4] rfold_last --- library/core/src/iter/traits/double_ended.rs | 38 ++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index bdf94c792c27c..40ca3d4566c14 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -301,6 +301,44 @@ 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)) + } + /// 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 From bd351615cbdcfd404b6c9445db8f77ade3f70ceb Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 1 Jan 2023 12:14:37 +0000 Subject: [PATCH 4/4] try_rfold_last --- library/core/src/iter/traits/double_ended.rs | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index 40ca3d4566c14..dbaea89d35e78 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -339,6 +339,33 @@ pub trait DoubleEndedIterator: Iterator { 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