From 246b844749eacc1fc25d93b40e99b40c6c54545a Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Mon, 21 Jul 2025 21:02:47 -0400 Subject: [PATCH 1/2] implement Sum and Product for Saturating(u*) --- library/core/src/iter/traits/accum.rs | 56 ++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/library/core/src/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs index 12e2b8b393a88..8176b22464499 100644 --- a/library/core/src/iter/traits/accum.rs +++ b/library/core/src/iter/traits/accum.rs @@ -1,5 +1,5 @@ use crate::iter; -use crate::num::Wrapping; +use crate::num::{Saturating, Wrapping}; /// Trait to represent types that can be created by summing up an iterator. /// @@ -98,6 +98,59 @@ macro_rules! integer_sum_product { ); } +macro_rules! saturating_integer_sum_product { + (@impls $zero:expr, $one:expr, #[$attr:meta], $($a:ty)*) => ($( + #[$attr] + impl Sum for $a { + fn sum>(iter: I) -> Self { + iter.fold( + $zero, + #[rustc_inherit_overflow_checks] + |a, b| a + b, + ) + } + } + + #[$attr] + impl Product for $a { + fn product>(iter: I) -> Self { + iter.fold( + $one, + #[rustc_inherit_overflow_checks] + |a, b| a * b, + ) + } + } + + #[$attr] + impl<'a> Sum<&'a $a> for $a { + fn sum>(iter: I) -> Self { + iter.fold( + $zero, + #[rustc_inherit_overflow_checks] + |a, b| a + b, + ) + } + } + + #[$attr] + impl<'a> Product<&'a $a> for $a { + fn product>(iter: I) -> Self { + iter.fold( + $one, + #[rustc_inherit_overflow_checks] + |a, b| a * b, + ) + } + } + )*); + ($($a:ty)*) => ( + integer_sum_product!(@impls Saturating(0), Saturating(1), + #[stable(feature = "saturating_iter_arith", since = "1.14.0")], + $(Saturating<$a>)*); + ); +} + macro_rules! float_sum_product { ($($a:ident)*) => ($( #[stable(feature = "iter_arith_traits", since = "1.12.0")] @@ -147,6 +200,7 @@ macro_rules! float_sum_product { } integer_sum_product! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +saturating_integer_sum_product! { u8 u16 u32 u64 u128 usize } float_sum_product! { f32 f64 } #[stable(feature = "iter_arith_traits_result", since = "1.16.0")] From d7a57506fdbb22420e8ed5897b5931fb929b4237 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Tue, 22 Jul 2025 19:05:52 -0400 Subject: [PATCH 2/2] add docs, test --- library/core/src/iter/traits/accum.rs | 11 ++++-- library/coretests/tests/iter/traits/accum.rs | 36 ++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/library/core/src/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs index 8176b22464499..52451ff24576d 100644 --- a/library/core/src/iter/traits/accum.rs +++ b/library/core/src/iter/traits/accum.rs @@ -99,8 +99,9 @@ macro_rules! integer_sum_product { } macro_rules! saturating_integer_sum_product { - (@impls $zero:expr, $one:expr, #[$attr:meta], $($a:ty)*) => ($( + (@impls $zero:expr, $one:expr, $doc:expr, #[$attr:meta], $($a:ty)*) => ($( #[$attr] + #[doc = $doc] impl Sum for $a { fn sum>(iter: I) -> Self { iter.fold( @@ -112,6 +113,7 @@ macro_rules! saturating_integer_sum_product { } #[$attr] + #[doc = $doc] impl Product for $a { fn product>(iter: I) -> Self { iter.fold( @@ -123,6 +125,7 @@ macro_rules! saturating_integer_sum_product { } #[$attr] + #[doc = $doc] impl<'a> Sum<&'a $a> for $a { fn sum>(iter: I) -> Self { iter.fold( @@ -134,6 +137,7 @@ macro_rules! saturating_integer_sum_product { } #[$attr] + #[doc = $doc] impl<'a> Product<&'a $a> for $a { fn product>(iter: I) -> Self { iter.fold( @@ -145,8 +149,9 @@ macro_rules! saturating_integer_sum_product { } )*); ($($a:ty)*) => ( - integer_sum_product!(@impls Saturating(0), Saturating(1), - #[stable(feature = "saturating_iter_arith", since = "1.14.0")], + saturating_integer_sum_product!(@impls Saturating(0), Saturating(1), + "The short-circuiting behavior of this implementation is unspecified. If you care about short-circuiting, use [`Iterator::fold`] directly.", + #[stable(feature = "saturating_iter_arith", since = "CURRENT_RUSTC_VERSION")], $(Saturating<$a>)*); ); } diff --git a/library/coretests/tests/iter/traits/accum.rs b/library/coretests/tests/iter/traits/accum.rs index f3eeb31fe5803..95f299d2680f8 100644 --- a/library/coretests/tests/iter/traits/accum.rs +++ b/library/coretests/tests/iter/traits/accum.rs @@ -1,4 +1,5 @@ use core::iter::*; +use std::num::Saturating; #[test] fn test_iterator_sum() { @@ -64,3 +65,38 @@ 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_saturating_sum_product() { + let v = (1u32..=10).map(|i| Saturating(i)); + assert_eq!(v.sum::>(), Saturating(55)); + let v = (1u32..=10).map(|i| Saturating(i)); + assert_eq!(v.product::>(), Saturating(3628800)); + let v = [Saturating(usize::MAX), Saturating(2)]; + assert_eq!(v.iter().copied().sum::>(), Saturating(usize::MAX)); + assert_eq!(v.iter().copied().product::>(), Saturating(usize::MAX)); + + let mut cnt = 0; + let v = 250..=u8::MAX; + assert_eq!( + v.map(|i| { + cnt += 1; + Saturating(i) + }) + .sum::>(), + Saturating(u8::MAX) + ); + assert_eq!(cnt, 6); // no short-circuiting + + let mut cnt = 0; + let v = (250..=u8::MAX).chain(0..5); + assert_eq!( + v.map(|i| { + cnt += 1; + Saturating(i) + }) + .product::>(), + Saturating(0) + ); + assert_eq!(cnt, 11); // no short-circuiting +}