Skip to content

Commit 2e41ba8

Browse files
committed
Use internal iteration in the Sum and Product impls of Result and Option
1 parent 4560cb8 commit 2e41ba8

File tree

6 files changed

+67
-119
lines changed

6 files changed

+67
-119
lines changed

src/libcore/iter/adapters/mod.rs

+37-109
Original file line numberDiff line numberDiff line change
@@ -2063,137 +2063,65 @@ impl<I: ExactSizeIterator, F> ExactSizeIterator for Inspect<I, F>
20632063
impl<I: FusedIterator, F> FusedIterator for Inspect<I, F>
20642064
where F: FnMut(&I::Item) {}
20652065

2066-
/// An iterator adapter that produces output as long as the underlying
2067-
/// iterator produces `Option::Some` values.
2068-
pub(crate) struct OptionShunt<I> {
2069-
iter: I,
2070-
exited_early: bool,
2071-
}
2072-
2073-
impl<I, T> OptionShunt<I>
2074-
where
2075-
I: Iterator<Item = Option<T>>,
2076-
{
2077-
/// Process the given iterator as if it yielded a `T` instead of a
2078-
/// `Option<T>`. Any `None` value will stop the inner iterator and
2079-
/// the overall result will be a `None`.
2080-
pub fn process<F, U>(iter: I, mut f: F) -> Option<U>
2081-
where
2082-
F: FnMut(&mut Self) -> U,
2083-
{
2084-
let mut shunt = OptionShunt::new(iter);
2085-
let value = f(shunt.by_ref());
2086-
shunt.reconstruct(value)
2087-
}
2088-
2089-
fn new(iter: I) -> Self {
2090-
OptionShunt {
2091-
iter,
2092-
exited_early: false,
2093-
}
2094-
}
2095-
2096-
/// Consume the adapter and rebuild a `Option` value.
2097-
fn reconstruct<U>(self, val: U) -> Option<U> {
2098-
if self.exited_early {
2099-
None
2100-
} else {
2101-
Some(val)
2102-
}
2103-
}
2104-
}
2105-
2106-
impl<I, T> Iterator for OptionShunt<I>
2107-
where
2108-
I: Iterator<Item = Option<T>>,
2109-
{
2110-
type Item = T;
2111-
2112-
fn next(&mut self) -> Option<Self::Item> {
2113-
match self.iter.next() {
2114-
Some(Some(v)) => Some(v),
2115-
Some(None) => {
2116-
self.exited_early = true;
2117-
None
2118-
}
2119-
None => None,
2120-
}
2121-
}
2122-
2123-
fn size_hint(&self) -> (usize, Option<usize>) {
2124-
if self.exited_early {
2125-
(0, Some(0))
2126-
} else {
2127-
let (_, upper) = self.iter.size_hint();
2128-
(0, upper)
2129-
}
2130-
}
2131-
}
2132-
21332066
/// An iterator adapter that produces output as long as the underlying
21342067
/// iterator produces `Result::Ok` values.
21352068
///
21362069
/// If an error is encountered, the iterator stops and the error is
2137-
/// stored. The error may be recovered later via `reconstruct`.
2138-
pub(crate) struct ResultShunt<I, E> {
2070+
/// stored.
2071+
pub(crate) struct ResultShunt<'a, I, E> {
21392072
iter: I,
2140-
error: Option<E>,
2073+
error: &'a mut Result<(), E>,
21412074
}
21422075

2143-
impl<I, T, E> ResultShunt<I, E>
2144-
where I: Iterator<Item = Result<T, E>>
2076+
/// Process the given iterator as if it yielded a `T` instead of a
2077+
/// `Result<T, _>`. Any errors will stop the inner iterator and
2078+
/// the overall result will be an error.
2079+
pub(crate) fn process_results<I, T, E, F, U>(iter: I, mut f: F) -> Result<U, E>
2080+
where
2081+
I: Iterator<Item = Result<T, E>>,
2082+
for<'a> F: FnMut(ResultShunt<'a, I, E>) -> U,
21452083
{
2146-
/// Process the given iterator as if it yielded a `T` instead of a
2147-
/// `Result<T, _>`. Any errors will stop the inner iterator and
2148-
/// the overall result will be an error.
2149-
pub fn process<F, U>(iter: I, mut f: F) -> Result<U, E>
2150-
where F: FnMut(&mut Self) -> U
2151-
{
2152-
let mut shunt = ResultShunt::new(iter);
2153-
let value = f(shunt.by_ref());
2154-
shunt.reconstruct(value)
2155-
}
2156-
2157-
fn new(iter: I) -> Self {
2158-
ResultShunt {
2159-
iter,
2160-
error: None,
2161-
}
2162-
}
2163-
2164-
/// Consume the adapter and rebuild a `Result` value. This should
2165-
/// *always* be called, otherwise any potential error would be
2166-
/// lost.
2167-
fn reconstruct<U>(self, val: U) -> Result<U, E> {
2168-
match self.error {
2169-
None => Ok(val),
2170-
Some(e) => Err(e),
2171-
}
2172-
}
2084+
let mut error = Ok(());
2085+
let shunt = ResultShunt {
2086+
iter,
2087+
error: &mut error,
2088+
};
2089+
let value = f(shunt);
2090+
error.map(|()| value)
21732091
}
21742092

2175-
impl<I, T, E> Iterator for ResultShunt<I, E>
2093+
impl<I, T, E> Iterator for ResultShunt<'_, I, E>
21762094
where I: Iterator<Item = Result<T, E>>
21772095
{
21782096
type Item = T;
21792097

21802098
fn next(&mut self) -> Option<Self::Item> {
2181-
match self.iter.next() {
2182-
Some(Ok(v)) => Some(v),
2183-
Some(Err(e)) => {
2184-
self.error = Some(e);
2185-
None
2186-
}
2187-
None => None,
2188-
}
2099+
self.find(|_| true)
21892100
}
21902101

21912102
fn size_hint(&self) -> (usize, Option<usize>) {
2192-
if self.error.is_some() {
2103+
if self.error.is_err() {
21932104
(0, Some(0))
21942105
} else {
21952106
let (_, upper) = self.iter.size_hint();
21962107
(0, upper)
21972108
}
21982109
}
2110+
2111+
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
2112+
where
2113+
F: FnMut(B, Self::Item) -> R,
2114+
R: Try<Ok = B>,
2115+
{
2116+
let error = &mut *self.error;
2117+
self.iter
2118+
.try_fold(init, |acc, x| match x {
2119+
Ok(x) => LoopState::from_try(f(acc, x)),
2120+
Err(e) => {
2121+
*error = Err(e);
2122+
LoopState::Break(Try::from_ok(acc))
2123+
}
2124+
})
2125+
.into_try()
2126+
}
21992127
}

src/libcore/iter/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ pub use self::adapters::Flatten;
360360
#[stable(feature = "iter_copied", since = "1.36.0")]
361361
pub use self::adapters::Copied;
362362

363-
pub(crate) use self::adapters::{TrustedRandomAccess, OptionShunt, ResultShunt};
363+
pub(crate) use self::adapters::{TrustedRandomAccess, process_results};
364364

365365
mod range;
366366
mod sources;

src/libcore/iter/traits/accum.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::ops::{Mul, Add};
22
use crate::num::Wrapping;
3-
use crate::iter::adapters::{OptionShunt, ResultShunt};
3+
use crate::iter;
44

55
/// Trait to represent types that can be created by summing up an iterator.
66
///
@@ -139,7 +139,7 @@ impl<T, U, E> Sum<Result<U, E>> for Result<T, E>
139139
fn sum<I>(iter: I) -> Result<T, E>
140140
where I: Iterator<Item = Result<U, E>>,
141141
{
142-
ResultShunt::process(iter, |i| i.sum())
142+
iter::process_results(iter, |i| i.sum())
143143
}
144144
}
145145

@@ -153,7 +153,7 @@ impl<T, U, E> Product<Result<U, E>> for Result<T, E>
153153
fn product<I>(iter: I) -> Result<T, E>
154154
where I: Iterator<Item = Result<U, E>>,
155155
{
156-
ResultShunt::process(iter, |i| i.product())
156+
iter::process_results(iter, |i| i.product())
157157
}
158158
}
159159

@@ -180,7 +180,7 @@ where
180180
where
181181
I: Iterator<Item = Option<U>>,
182182
{
183-
OptionShunt::process(iter, |i| i.sum())
183+
iter.map(|x| x.ok_or(())).sum::<Result<_, _>>().ok()
184184
}
185185
}
186186

@@ -196,6 +196,6 @@ where
196196
where
197197
I: Iterator<Item = Option<U>>,
198198
{
199-
OptionShunt::process(iter, |i| i.product())
199+
iter.map(|x| x.ok_or(())).product::<Result<_, _>>().ok()
200200
}
201201
}

src/libcore/option.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
136136
#![stable(feature = "rust1", since = "1.0.0")]
137137

138-
use crate::iter::{FromIterator, FusedIterator, TrustedLen, OptionShunt};
138+
use crate::iter::{FromIterator, FusedIterator, TrustedLen};
139139
use crate::{convert, fmt, hint, mem, ops::{self, Deref, DerefMut}};
140140
use crate::pin::Pin;
141141

@@ -1499,7 +1499,10 @@ impl<A, V: FromIterator<A>> FromIterator<Option<A>> for Option<V> {
14991499
// FIXME(#11084): This could be replaced with Iterator::scan when this
15001500
// performance bug is closed.
15011501

1502-
OptionShunt::process(iter.into_iter(), |i| i.collect())
1502+
iter.into_iter()
1503+
.map(|x| x.ok_or(()))
1504+
.collect::<Result<_, _>>()
1505+
.ok()
15031506
}
15041507
}
15051508

src/libcore/result.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@
231231
#![stable(feature = "rust1", since = "1.0.0")]
232232

233233
use crate::fmt;
234-
use crate::iter::{FromIterator, FusedIterator, TrustedLen, ResultShunt};
234+
use crate::iter::{self, FromIterator, FusedIterator, TrustedLen};
235235
use crate::ops::{self, Deref, DerefMut};
236236

237237
/// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]).
@@ -1343,7 +1343,7 @@ impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> {
13431343
// FIXME(#11084): This could be replaced with Iterator::scan when this
13441344
// performance bug is closed.
13451345

1346-
ResultShunt::process(iter.into_iter(), |i| i.collect())
1346+
iter::process_results(iter.into_iter(), |i| i.collect())
13471347
}
13481348
}
13491349

src/libcore/tests/iter.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,23 @@ fn test_iterator_sum_result() {
10821082
assert_eq!(v.iter().cloned().sum::<Result<i32, _>>(), Ok(10));
10831083
let v: &[Result<i32, ()>] = &[Ok(1), Err(()), Ok(3), Ok(4)];
10841084
assert_eq!(v.iter().cloned().sum::<Result<i32, _>>(), Err(()));
1085+
1086+
#[derive(PartialEq, Debug)]
1087+
struct S(Result<i32, ()>);
1088+
1089+
impl Sum<Result<i32, ()>> for S {
1090+
fn sum<I: Iterator<Item = Result<i32, ()>>>(mut iter: I) -> Self {
1091+
// takes the sum by repeatedly calling `next` on `iter`,
1092+
// thus testing that repeated calls to `ResultShunt::try_fold`
1093+
// produce the expected results
1094+
Self(iter.by_ref().sum())
1095+
}
1096+
}
1097+
1098+
let v: &[Result<i32, ()>] = &[Ok(1), Ok(2), Ok(3), Ok(4)];
1099+
assert_eq!(v.iter().cloned().sum::<S>(), S(Ok(10)));
1100+
let v: &[Result<i32, ()>] = &[Ok(1), Err(()), Ok(3), Ok(4)];
1101+
assert_eq!(v.iter().cloned().sum::<S>(), S(Err(())));
10851102
}
10861103

10871104
#[test]

0 commit comments

Comments
 (0)