Skip to content

Commit d67d1f2

Browse files
authored
Rollup merge of rust-lang#58975 - jtdowney:iter_arith_traits_option, r=dtolnay
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. I based a lot of this on rust-lang#38580. From that discussion it didn't seem like this addition would be too controversial or difficult. One thing I still don't understand is picking the values for the `stable` attribute. This is my first non-documentation PR for rust so I am open to any feedback on improvements.
2 parents 4b9d803 + 9f8d934 commit d67d1f2

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed

src/libcore/iter/traits/accum.rs

+110
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,113 @@ impl<T, U, E> Product<Result<U, E>> for Result<T, E>
223223
ResultShunt::process(iter, |i| i.product())
224224
}
225225
}
226+
227+
/// An iterator adapter that produces output as long as the underlying
228+
/// iterator produces `Option::Some` values.
229+
struct OptionShunt<I> {
230+
iter: I,
231+
exited_early: bool,
232+
}
233+
234+
impl<I, T> OptionShunt<I>
235+
where
236+
I: Iterator<Item = Option<T>>,
237+
{
238+
/// Process the given iterator as if it yielded a `T` instead of a
239+
/// `Option<T>`. Any `None` value will stop the inner iterator and
240+
/// the overall result will be a `None`.
241+
pub fn process<F, U>(iter: I, mut f: F) -> Option<U>
242+
where
243+
F: FnMut(&mut Self) -> U,
244+
{
245+
let mut shunt = OptionShunt::new(iter);
246+
let value = f(shunt.by_ref());
247+
shunt.reconstruct(value)
248+
}
249+
250+
fn new(iter: I) -> Self {
251+
OptionShunt {
252+
iter,
253+
exited_early: false,
254+
}
255+
}
256+
257+
/// Consume the adapter and rebuild a `Option` value.
258+
fn reconstruct<U>(self, val: U) -> Option<U> {
259+
if self.exited_early {
260+
None
261+
} else {
262+
Some(val)
263+
}
264+
}
265+
}
266+
267+
impl<I, T> Iterator for OptionShunt<I>
268+
where
269+
I: Iterator<Item = Option<T>>,
270+
{
271+
type Item = T;
272+
273+
fn next(&mut self) -> Option<Self::Item> {
274+
match self.iter.next() {
275+
Some(Some(v)) => Some(v),
276+
Some(None) => {
277+
self.exited_early = true;
278+
None
279+
}
280+
None => None,
281+
}
282+
}
283+
284+
fn size_hint(&self) -> (usize, Option<usize>) {
285+
if self.exited_early {
286+
(0, Some(0))
287+
} else {
288+
let (_, upper) = self.iter.size_hint();
289+
(0, upper)
290+
}
291+
}
292+
}
293+
294+
#[stable(feature = "iter_arith_traits_option", since = "1.37.0")]
295+
impl<T, U> Sum<Option<U>> for Option<T>
296+
where
297+
T: Sum<U>,
298+
{
299+
/// Takes each element in the `Iterator`: if it is a `None`, no further
300+
/// elements are taken, and the `None` is returned. Should no `None` occur,
301+
/// the sum of all elements is returned.
302+
///
303+
/// # Examples
304+
///
305+
/// This sums up the position of the character 'a' in a vector of strings,
306+
/// if a word did not have the character 'a' the operation returns `None`:
307+
///
308+
/// ```
309+
/// let words = vec!["have", "a", "great", "day"];
310+
/// let total: Option<usize> = words.iter().map(|w| w.find('a')).sum();
311+
/// assert_eq!(total, Some(5));
312+
/// ```
313+
fn sum<I>(iter: I) -> Option<T>
314+
where
315+
I: Iterator<Item = Option<U>>,
316+
{
317+
OptionShunt::process(iter, |i| i.sum())
318+
}
319+
}
320+
321+
#[stable(feature = "iter_arith_traits_option", since = "1.37.0")]
322+
impl<T, U> Product<Option<U>> for Option<T>
323+
where
324+
T: Product<U>,
325+
{
326+
/// Takes each element in the `Iterator`: if it is a `None`, no further
327+
/// elements are taken, and the `None` is returned. Should no `None` occur,
328+
/// the product of all elements is returned.
329+
fn product<I>(iter: I) -> Option<T>
330+
where
331+
I: Iterator<Item = Option<U>>,
332+
{
333+
OptionShunt::process(iter, |i| i.product())
334+
}
335+
}

src/libcore/tests/iter.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,14 @@ fn test_iterator_sum_result() {
10841084
assert_eq!(v.iter().cloned().sum::<Result<i32, _>>(), Err(()));
10851085
}
10861086

1087+
#[test]
1088+
fn test_iterator_sum_option() {
1089+
let v: &[Option<i32>] = &[Some(1), Some(2), Some(3), Some(4)];
1090+
assert_eq!(v.iter().cloned().sum::<Option<i32>>(), Some(10));
1091+
let v: &[Option<i32>] = &[Some(1), None, Some(3), Some(4)];
1092+
assert_eq!(v.iter().cloned().sum::<Option<i32>>(), None);
1093+
}
1094+
10871095
#[test]
10881096
fn test_iterator_product() {
10891097
let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
@@ -1126,6 +1134,14 @@ impl Ord for Mod3 {
11261134
}
11271135
}
11281136

1137+
#[test]
1138+
fn test_iterator_product_option() {
1139+
let v: &[Option<i32>] = &[Some(1), Some(2), Some(3), Some(4)];
1140+
assert_eq!(v.iter().cloned().product::<Option<i32>>(), Some(24));
1141+
let v: &[Option<i32>] = &[Some(1), None, Some(3), Some(4)];
1142+
assert_eq!(v.iter().cloned().product::<Option<i32>>(), None);
1143+
}
1144+
11291145
#[test]
11301146
fn test_iterator_max() {
11311147
let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

0 commit comments

Comments
 (0)