From a35cdd4e41a2bec5d8896653f898aaff61cb0106 Mon Sep 17 00:00:00 2001 From: John Downey Date: Wed, 6 Mar 2019 12:17:26 -0600 Subject: [PATCH 1/4] Implement `iter::Sum` and `iter::Product` for `Option` This is similar to the existing implementation for `Result`. It will take each item into the accumulator unless a `None` is returned. --- src/libcore/iter/traits/accum.rs | 113 +++++++++++++++++++++++++++++++ src/libcore/tests/iter.rs | 16 +++++ 2 files changed, 129 insertions(+) diff --git a/src/libcore/iter/traits/accum.rs b/src/libcore/iter/traits/accum.rs index dfe1d2a1006d7..b67714ca77903 100644 --- a/src/libcore/iter/traits/accum.rs +++ b/src/libcore/iter/traits/accum.rs @@ -223,3 +223,116 @@ impl Product> for Result ResultShunt::process(iter, |i| i.product()) } } + +/// An iterator adapter that produces output as long as the underlying +/// iterator produces `Option::Some` values. +struct OptionShunt { + iter: I, + exited_early: bool, +} + +impl OptionShunt +where + I: Iterator>, +{ + /// Process the given iterator as if it yielded a `T` instead of a + /// `Option`. Any `None` value will stop the inner iterator and + /// the overall result will be a `None`. + pub fn process(iter: I, mut f: F) -> Option + where + F: FnMut(&mut Self) -> U, + { + let mut shunt = OptionShunt::new(iter); + let value = f(shunt.by_ref()); + shunt.reconstruct(value) + } + + fn new(iter: I) -> Self { + OptionShunt { + iter, + exited_early: false, + } + } + + /// Consume the adapter and rebuild a `Option` value. + fn reconstruct(self, val: U) -> Option { + if self.exited_early { + None + } else { + Some(val) + } + } +} + +impl Iterator for OptionShunt +where + I: Iterator>, +{ + type Item = T; + + fn next(&mut self) -> Option { + match self.iter.next() { + Some(Some(v)) => Some(v), + Some(None) => { + self.exited_early = true; + None + } + None => None, + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.exited_early { + (0, Some(0)) + } else { + let (_, upper) = self.iter.size_hint(); + (0, upper) + } + } +} + +#[stable(feature = "iter_arith_traits_option", since = "1.34.0")] +impl Sum> for Option +where + T: Sum, +{ + /// Takes each element in the `Iterator`: if it is a `None`, no further + /// elements are taken, and the `None` is returned. Should no `None` occur, + /// the sum of all elements is returned. + /// + /// # Examples + /// + /// This sums up every integer in a vector, rejecting the sum if a negative + /// element is encountered: + /// + /// ``` + /// let v = vec![1, 2]; + /// let res: Option = v.iter().map(|&x: &i32| + /// if x < 0 { None } + /// else { Some(x) } + /// ).sum(); + /// assert_eq!(res, Some(3)); + /// ``` + fn sum(iter: I) -> Option + where + I: Iterator>, + { + OptionShunt::process(iter, |i| i.sum()) + } +} + +#[stable(feature = "iter_arith_traits_option", since = "1.34.0")] +impl Product> for Option +where + T: Product, +{ + /// Takes each element in the `Iterator`: if it is a `None`, no further + /// elements are taken, and the `None` is returned. Should no `None` occur, + /// the product of all elements is returned. + fn product(iter: I) -> Option + where + I: Iterator>, + { + OptionShunt::process(iter, |i| i.product()) + } +} diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index d880abb181c20..814f4ec4ca53b 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -1066,6 +1066,14 @@ fn test_iterator_sum_result() { assert_eq!(v.iter().cloned().sum::>(), Err(())); } +#[test] +fn test_iterator_sum_option() { + let v: &[Option] = &[Some(1), Some(2), Some(3), Some(4)]; + assert_eq!(v.iter().cloned().sum::>(), Some(10)); + let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; + assert_eq!(v.iter().cloned().sum::>(), None); +} + #[test] fn test_iterator_product() { let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; @@ -1082,6 +1090,14 @@ fn test_iterator_product_result() { assert_eq!(v.iter().cloned().product::>(), Err(())); } +#[test] +fn test_iterator_product_option() { + let v: &[Option] = &[Some(1), Some(2), Some(3), Some(4)]; + assert_eq!(v.iter().cloned().product::>(), Some(24)); + let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; + assert_eq!(v.iter().cloned().product::>(), None); +} + #[test] fn test_iterator_max() { let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; From 8c698763579d5cf78094d2d4495c3d801ed0d2e7 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 19 Mar 2019 17:17:18 -0500 Subject: [PATCH 2/4] Update stable attribute to be since 1.35.0 Co-Authored-By: jtdowney --- src/libcore/iter/traits/accum.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/iter/traits/accum.rs b/src/libcore/iter/traits/accum.rs index b67714ca77903..ae51e1bddac0a 100644 --- a/src/libcore/iter/traits/accum.rs +++ b/src/libcore/iter/traits/accum.rs @@ -291,7 +291,7 @@ where } } -#[stable(feature = "iter_arith_traits_option", since = "1.34.0")] +#[stable(feature = "iter_arith_traits_option", since = "1.35.0")] impl Sum> for Option where T: Sum, @@ -321,7 +321,7 @@ where } } -#[stable(feature = "iter_arith_traits_option", since = "1.34.0")] +#[stable(feature = "iter_arith_traits_option", since = "1.35.0")] impl Product> for Option where T: Product, From 422a4c07c826fb007f5199031482d5b40d255899 Mon Sep 17 00:00:00 2001 From: John Downey Date: Wed, 20 Mar 2019 20:14:46 -0500 Subject: [PATCH 3/4] Add improved doc example for Sum> --- src/libcore/iter/traits/accum.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/libcore/iter/traits/accum.rs b/src/libcore/iter/traits/accum.rs index ae51e1bddac0a..88c4820d5839c 100644 --- a/src/libcore/iter/traits/accum.rs +++ b/src/libcore/iter/traits/accum.rs @@ -302,16 +302,13 @@ where /// /// # Examples /// - /// This sums up every integer in a vector, rejecting the sum if a negative - /// element is encountered: + /// This sums up the position of the character 'a' in a vector of strings, + /// if a word did not have the character 'a' the operation returns `None`: /// /// ``` - /// let v = vec![1, 2]; - /// let res: Option = v.iter().map(|&x: &i32| - /// if x < 0 { None } - /// else { Some(x) } - /// ).sum(); - /// assert_eq!(res, Some(3)); + /// let words = vec!["have", "a", "great", "day"]; + /// let total: Option = words.iter().map(|w| w.find('a')).sum(); + /// assert_eq!(total, Some(5)); /// ``` fn sum(iter: I) -> Option where From 9f8d934f271fcaf9c34aefa8062b7563cfd34721 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 28 May 2019 16:00:39 -0700 Subject: [PATCH 4/4] Bump Sum and Product for Option to 1.37 --- src/libcore/iter/traits/accum.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/iter/traits/accum.rs b/src/libcore/iter/traits/accum.rs index 88c4820d5839c..f9e86be3721a5 100644 --- a/src/libcore/iter/traits/accum.rs +++ b/src/libcore/iter/traits/accum.rs @@ -291,7 +291,7 @@ where } } -#[stable(feature = "iter_arith_traits_option", since = "1.35.0")] +#[stable(feature = "iter_arith_traits_option", since = "1.37.0")] impl Sum> for Option where T: Sum, @@ -318,7 +318,7 @@ where } } -#[stable(feature = "iter_arith_traits_option", since = "1.35.0")] +#[stable(feature = "iter_arith_traits_option", since = "1.37.0")] impl Product> for Option where T: Product,