From 915a97560833bfc941753a3d5d80fd9ca70cefc0 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 30 Mar 2022 17:10:14 +0100 Subject: [PATCH] Add `Iterator::checked_{sum, product}` --- library/core/src/iter/mod.rs | 2 + library/core/src/iter/traits/accum.rs | 67 ++++++++++++++++++++++++ library/core/src/iter/traits/iterator.rs | 55 ++++++++++++++++++- library/core/src/iter/traits/mod.rs | 2 + library/core/tests/iter/traits/accum.rs | 38 ++++++++++++++ library/core/tests/lib.rs | 1 + 6 files changed, 164 insertions(+), 1 deletion(-) diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 22b76ea66ff15..9585a643e8bfd 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -385,6 +385,8 @@ pub use self::traits::InPlaceIterable; pub use self::traits::TrustedLen; #[unstable(feature = "trusted_step", issue = "85731")] pub use self::traits::TrustedStep; +#[unstable(feature = "checked_sum_product", issue = "95484")] +pub use self::traits::{CheckedProduct, CheckedSum}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::traits::{ DoubleEndedIterator, ExactSizeIterator, Extend, FromIterator, IntoIterator, Product, Sum, diff --git a/library/core/src/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs index 84d83ee39699f..fbd2c7d7cd35c 100644 --- a/library/core/src/iter/traits/accum.rs +++ b/library/core/src/iter/traits/accum.rs @@ -34,6 +34,40 @@ pub trait Product: Sized { fn product>(iter: I) -> Self; } +/// Trait to represent types that can be created by summing up an iterator, +/// with overflow checks. +/// +/// This trait is used to implement [`Iterator::checked_sum()`]. Types which implement +/// this trait can be generated by using the [`checked_sum()`] method on an iterator. +/// Like [`FromIterator`], this trait should rarely be called directly. +/// +/// [`checked_sum()`]: Iterator::checked_sum +/// [`FromIterator`]: iter::FromIterator +#[unstable(feature = "checked_sum_product", issue = "95484")] +pub trait CheckedSum: Sized { + /// Method which takes an iterator and generates `Some(Self)` from the elements by + /// "summing up" the items, or `None` if overflow occurs. + #[unstable(feature = "checked_sum_product", issue = "95484")] + fn checked_sum>(iter: I) -> Option; +} + +/// Trait to represent types that can be created by multiplying elements of an +/// iterator, with overflow checks. +/// +/// This trait is used to implement [`Iterator::checked_product()`]. Types which implement +/// this trait can be generated by using the [`checked_product()`] method on an iterator. +/// Like [`FromIterator`], this trait should rarely be called directly. +/// +/// [`checked_product()`]: Iterator::checked_product +/// [`FromIterator`]: iter::FromIterator +#[unstable(feature = "checked_sum_product", issue = "95484")] +pub trait CheckedProduct: Sized { + /// Method which takes an iterator and generates `Some(Self)` from the elements by + /// multiplying the items, or `None` if overflow occurs. + #[unstable(feature = "checked_sum_product", issue = "95484")] + fn checked_product>(iter: I) -> Option; +} + macro_rules! integer_sum_product { (@impls $zero:expr, $one:expr, #[$attr:meta], $($a:ty)*) => ($( #[$attr] @@ -90,6 +124,38 @@ macro_rules! integer_sum_product { ); } +macro_rules! integer_checked_sum_product { + ($($a:ident)*) => ($( + #[unstable(feature = "checked_sum_product", issue = "95484")] + impl CheckedSum for $a { + fn checked_sum>(mut iter: I) -> Option { + iter.try_fold(0, |a: $a, b: $a| a.checked_add(b)) + } + } + + #[unstable(feature = "checked_sum_product", issue = "95484")] + impl<'a> CheckedSum<&'a $a> for $a { + fn checked_sum>(mut iter: I) -> Option { + iter.try_fold(0, |a: $a, b: &$a| a.checked_add(*b)) + } + } + + #[unstable(feature = "checked_sum_product", issue = "95484")] + impl CheckedProduct for $a { + fn checked_product>(mut iter: I) -> Option { + iter.try_fold(1, |a: $a, b: $a| a.checked_mul(b)) + } + } + + #[unstable(feature = "checked_sum_product", issue = "95484")] + impl<'a> CheckedProduct<&'a $a> for $a { + fn checked_product>(mut iter: I) -> Option { + iter.try_fold(1, |a: $a, b: &$a| a.checked_mul(*b)) + } + } + )*) +} + macro_rules! float_sum_product { ($($a:ident)*) => ($( #[stable(feature = "iter_arith_traits", since = "1.12.0")] @@ -139,6 +205,7 @@ macro_rules! float_sum_product { } integer_sum_product! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +integer_checked_sum_product! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } float_sum_product! { f32 f64 } #[stable(feature = "iter_arith_traits_result", since = "1.16.0")] diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 53fbe4cbc42f5..0a5bb4d8bd978 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -5,8 +5,10 @@ use super::super::try_process; use super::super::ByRefSized; use super::super::TrustedRandomAccessNoCoerce; use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; +use super::super::{ + CheckedProduct, CheckedSum, FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip, +}; use super::super::{FlatMap, Flatten}; -use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip}; use super::super::{ Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile, }; @@ -3324,6 +3326,57 @@ pub trait Iterator { Product::product(self) } + /// Sums the elements of an iterator, returning `None` if overflow occored. + /// + /// An empty iterator returns the zero value of the type. + /// + /// The sum is computed left to right, so the order of the elements may + /// effect the sum for signed numbers. + /// + /// # Examples + /// + /// ``` + /// # #![feature(checked_sum_product)] + /// assert_eq!([1, 2, 3].iter().checked_sum::(), Some(6)); + /// assert_eq!([100, 20, 30].iter().checked_sum::(), None); + /// assert_eq!([100, -100, 100].iter().checked_sum::(), Some(100)); + /// assert_eq!([100, 100, -100].iter().checked_sum::(), None); + /// ``` + #[unstable(feature = "checked_sum_product", issue = "95484")] + fn checked_sum(self) -> Option + where + Self: Sized, + S: CheckedSum, + { + CheckedSum::checked_sum(self) + } + + /// Iterates over the entire iterator, multiplying all the elements, + /// returning `None` if overflow occored. + /// + /// An empty iterator returns the one value of the type. + /// + /// The product is computed left to right, so the order of the elements may + /// effect the sum for if their is a zero after overflow occors. + /// + /// # Examples + /// + /// ``` + /// # #![feature(checked_sum_product)] + /// assert_eq!([1, 2, 3].iter().checked_product::(), Some(6)); + /// assert_eq!([100, 20, 30].iter().checked_product::(), None); + /// assert_eq!([100, 0, 100].iter().checked_product::(), Some(0)); + /// assert_eq!([100, 100, 0].iter().checked_product::(), None); + /// ``` + #[unstable(feature = "checked_sum_product", issue = "95484")] + fn checked_product(self) -> Option + where + Self: Sized, + S: CheckedProduct, + { + CheckedProduct::checked_product(self) + } + /// [Lexicographically](Ord#lexicographical-comparison) compares the elements of this [`Iterator`] with those /// of another. /// diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs index ed0fb634dbf05..6586866d103e6 100644 --- a/library/core/src/iter/traits/mod.rs +++ b/library/core/src/iter/traits/mod.rs @@ -15,6 +15,8 @@ pub use self::{ marker::{FusedIterator, TrustedLen}, }; +#[unstable(feature = "checked_sum_product", issue = "95484")] +pub use self::accum::{CheckedProduct, CheckedSum}; #[unstable(issue = "none", feature = "inplace_iteration")] pub use self::marker::InPlaceIterable; #[unstable(feature = "trusted_step", issue = "85731")] diff --git a/library/core/tests/iter/traits/accum.rs b/library/core/tests/iter/traits/accum.rs index f3eeb31fe5803..6ac30cd67adfe 100644 --- a/library/core/tests/iter/traits/accum.rs +++ b/library/core/tests/iter/traits/accum.rs @@ -64,3 +64,41 @@ fn test_iterator_product_option() { let v: &[Option] = &[Some(1), None, Some(3), Some(4)]; assert_eq!(v.iter().cloned().product::>(), None); } + +#[test] +fn test_iterator_checked_sum() { + let v: &[i8] = &[1, 2, 3, 4, 5]; + assert_eq!(v.iter().checked_sum(), Some(15)); + assert_eq!(v.iter().copied().checked_sum(), Some(15)); + let v: &[i8] = &[10, 20, 30, 40, 50]; + assert_eq!(v.iter().checked_sum::(), None); + assert_eq!(v.iter().copied().checked_sum::(), None); + let v: &[i8] = &[100, -100, 100, -100]; + assert_eq!(v.iter().checked_sum(), Some(0)); + assert_eq!(v.iter().copied().checked_sum(), Some(0)); + let v: &[i8] = &[100, 100, -100, -100]; + assert_eq!(v.iter().checked_sum::(), None); + assert_eq!(v.iter().copied().checked_sum::(), None); + let v: &[i8] = &[]; + assert_eq!(v.iter().checked_sum::(), Some(0)); + assert_eq!(v.iter().copied().checked_sum::(), Some(0)); +} + +#[test] +fn test_iterator_checked_product() { + let v: &[i8] = &[1, 2, 3, 4, 5]; + assert_eq!(v.iter().checked_product(), Some(120)); + assert_eq!(v.iter().copied().checked_product(), Some(120)); + let v: &[i8] = &[10, 20, 30, 40, 50]; + assert_eq!(v.iter().checked_product::(), None); + assert_eq!(v.iter().copied().checked_product::(), None); + let v: &[i8] = &[100, 0, -100, 0, 10]; + assert_eq!(v.iter().checked_product(), Some(0)); + assert_eq!(v.iter().copied().checked_product(), Some(0)); + let v: &[i8] = &[100, -100, 0, 10, 0]; + assert_eq!(v.iter().checked_product::(), None); + assert_eq!(v.iter().copied().checked_product::(), None); + let v: &[i8] = &[]; + assert_eq!(v.iter().checked_product::(), Some(1)); + assert_eq!(v.iter().copied().checked_product::(), Some(1)); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 5f90a76ab74ee..b7a12cb7312ec 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -6,6 +6,7 @@ #![feature(bool_to_option)] #![feature(box_syntax)] #![feature(cell_update)] +#![feature(checked_sum_product)] #![feature(const_assume)] #![feature(const_black_box)] #![feature(const_bool_to_option)]