Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Iterator::checked_{sum, product} #95485

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions library/core/src/iter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
67 changes: 67 additions & 0 deletions library/core/src/iter/traits/accum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,40 @@ pub trait Product<A = Self>: Sized {
fn product<I: Iterator<Item = A>>(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<A = Self>: 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<I: Iterator<Item = A>>(iter: I) -> Option<Self>;
}

/// 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<A = Self>: 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<I: Iterator<Item = A>>(iter: I) -> Option<Self>;
}

macro_rules! integer_sum_product {
(@impls $zero:expr, $one:expr, #[$attr:meta], $($a:ty)*) => ($(
#[$attr]
Expand Down Expand Up @@ -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<I: Iterator<Item=Self>>(mut iter: I) -> Option<Self> {
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<I: Iterator<Item=&'a Self>>(mut iter: I) -> Option<Self> {
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<I: Iterator<Item=Self>>(mut iter: I) -> Option<Self> {
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<I: Iterator<Item=&'a Self>>(mut iter: I) -> Option<Self> {
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")]
Expand Down Expand Up @@ -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")]
Expand Down
55 changes: 54 additions & 1 deletion library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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::<i8>(), Some(6));
/// assert_eq!([100, 20, 30].iter().checked_sum::<i8>(), None);
/// assert_eq!([100, -100, 100].iter().checked_sum::<i8>(), Some(100));
/// assert_eq!([100, 100, -100].iter().checked_sum::<i8>(), None);
/// ```
#[unstable(feature = "checked_sum_product", issue = "95484")]
fn checked_sum<S>(self) -> Option<S>
where
Self: Sized,
S: CheckedSum<Self::Item>,
{
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::<i8>(), Some(6));
/// assert_eq!([100, 20, 30].iter().checked_product::<i8>(), None);
/// assert_eq!([100, 0, 100].iter().checked_product::<i8>(), Some(0));
/// assert_eq!([100, 100, 0].iter().checked_product::<i8>(), None);
/// ```
#[unstable(feature = "checked_sum_product", issue = "95484")]
fn checked_product<S>(self) -> Option<S>
where
Self: Sized,
S: CheckedProduct<Self::Item>,
{
CheckedProduct::checked_product(self)
}

/// [Lexicographically](Ord#lexicographical-comparison) compares the elements of this [`Iterator`] with those
/// of another.
///
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/iter/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
38 changes: 38 additions & 0 deletions library/core/tests/iter/traits/accum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,41 @@ fn test_iterator_product_option() {
let v: &[Option<i32>] = &[Some(1), None, Some(3), Some(4)];
assert_eq!(v.iter().cloned().product::<Option<i32>>(), 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::<i8>(), None);
assert_eq!(v.iter().copied().checked_sum::<i8>(), 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::<i8>(), None);
assert_eq!(v.iter().copied().checked_sum::<i8>(), None);
let v: &[i8] = &[];
assert_eq!(v.iter().checked_sum::<i8>(), Some(0));
assert_eq!(v.iter().copied().checked_sum::<i8>(), 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::<i8>(), None);
assert_eq!(v.iter().copied().checked_product::<i8>(), 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::<i8>(), None);
assert_eq!(v.iter().copied().checked_product::<i8>(), None);
let v: &[i8] = &[];
assert_eq!(v.iter().checked_product::<i8>(), Some(1));
assert_eq!(v.iter().copied().checked_product::<i8>(), Some(1));
}
1 change: 1 addition & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down