Skip to content

Commit f45cdea

Browse files
authored
Rollup merge of rust-lang#62459 - timvermeulen:result_sum_internal_iteration, r=scottmcm
Use internal iteration in the Sum and Product impls of Result and Option This PR adds internal iteration to the `ResultShunt` iterator type underlying the `Sum` and `Product` impls of `Result`. I had to change `ResultShunt` to hold a mutable reference to an error instead, similar to `itertools::ProcessResults`, in order to be able to pass the `ResultShunt` itself by value (which is necessary for internal iteration). `ResultShunt::process` can unfortunately no longer be an associated function because that would make it generic over the lifetime of the error reference, which wouldn't work, so I turned it into the free function `process_results`. I removed the `OptionShunt` type and forwarded the `Sum` and `Product` impls of `Option` to their respective impls of `Result` instead, to avoid having to repeat the internal iteration logic.
2 parents 8996328 + 2e41ba8 commit f45cdea

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
@@ -2180,137 +2180,65 @@ impl<I: ExactSizeIterator, F> ExactSizeIterator for Inspect<I, F>
21802180
impl<I: FusedIterator, F> FusedIterator for Inspect<I, F>
21812181
where F: FnMut(&I::Item) {}
21822182

2183-
/// An iterator adapter that produces output as long as the underlying
2184-
/// iterator produces `Option::Some` values.
2185-
pub(crate) struct OptionShunt<I> {
2186-
iter: I,
2187-
exited_early: bool,
2188-
}
2189-
2190-
impl<I, T> OptionShunt<I>
2191-
where
2192-
I: Iterator<Item = Option<T>>,
2193-
{
2194-
/// Process the given iterator as if it yielded a `T` instead of a
2195-
/// `Option<T>`. Any `None` value will stop the inner iterator and
2196-
/// the overall result will be a `None`.
2197-
pub fn process<F, U>(iter: I, mut f: F) -> Option<U>
2198-
where
2199-
F: FnMut(&mut Self) -> U,
2200-
{
2201-
let mut shunt = OptionShunt::new(iter);
2202-
let value = f(shunt.by_ref());
2203-
shunt.reconstruct(value)
2204-
}
2205-
2206-
fn new(iter: I) -> Self {
2207-
OptionShunt {
2208-
iter,
2209-
exited_early: false,
2210-
}
2211-
}
2212-
2213-
/// Consume the adapter and rebuild a `Option` value.
2214-
fn reconstruct<U>(self, val: U) -> Option<U> {
2215-
if self.exited_early {
2216-
None
2217-
} else {
2218-
Some(val)
2219-
}
2220-
}
2221-
}
2222-
2223-
impl<I, T> Iterator for OptionShunt<I>
2224-
where
2225-
I: Iterator<Item = Option<T>>,
2226-
{
2227-
type Item = T;
2228-
2229-
fn next(&mut self) -> Option<Self::Item> {
2230-
match self.iter.next() {
2231-
Some(Some(v)) => Some(v),
2232-
Some(None) => {
2233-
self.exited_early = true;
2234-
None
2235-
}
2236-
None => None,
2237-
}
2238-
}
2239-
2240-
fn size_hint(&self) -> (usize, Option<usize>) {
2241-
if self.exited_early {
2242-
(0, Some(0))
2243-
} else {
2244-
let (_, upper) = self.iter.size_hint();
2245-
(0, upper)
2246-
}
2247-
}
2248-
}
2249-
22502183
/// An iterator adapter that produces output as long as the underlying
22512184
/// iterator produces `Result::Ok` values.
22522185
///
22532186
/// If an error is encountered, the iterator stops and the error is
2254-
/// stored. The error may be recovered later via `reconstruct`.
2255-
pub(crate) struct ResultShunt<I, E> {
2187+
/// stored.
2188+
pub(crate) struct ResultShunt<'a, I, E> {
22562189
iter: I,
2257-
error: Option<E>,
2190+
error: &'a mut Result<(), E>,
22582191
}
22592192

2260-
impl<I, T, E> ResultShunt<I, E>
2261-
where I: Iterator<Item = Result<T, E>>
2193+
/// Process the given iterator as if it yielded a `T` instead of a
2194+
/// `Result<T, _>`. Any errors will stop the inner iterator and
2195+
/// the overall result will be an error.
2196+
pub(crate) fn process_results<I, T, E, F, U>(iter: I, mut f: F) -> Result<U, E>
2197+
where
2198+
I: Iterator<Item = Result<T, E>>,
2199+
for<'a> F: FnMut(ResultShunt<'a, I, E>) -> U,
22622200
{
2263-
/// Process the given iterator as if it yielded a `T` instead of a
2264-
/// `Result<T, _>`. Any errors will stop the inner iterator and
2265-
/// the overall result will be an error.
2266-
pub fn process<F, U>(iter: I, mut f: F) -> Result<U, E>
2267-
where F: FnMut(&mut Self) -> U
2268-
{
2269-
let mut shunt = ResultShunt::new(iter);
2270-
let value = f(shunt.by_ref());
2271-
shunt.reconstruct(value)
2272-
}
2273-
2274-
fn new(iter: I) -> Self {
2275-
ResultShunt {
2276-
iter,
2277-
error: None,
2278-
}
2279-
}
2280-
2281-
/// Consume the adapter and rebuild a `Result` value. This should
2282-
/// *always* be called, otherwise any potential error would be
2283-
/// lost.
2284-
fn reconstruct<U>(self, val: U) -> Result<U, E> {
2285-
match self.error {
2286-
None => Ok(val),
2287-
Some(e) => Err(e),
2288-
}
2289-
}
2201+
let mut error = Ok(());
2202+
let shunt = ResultShunt {
2203+
iter,
2204+
error: &mut error,
2205+
};
2206+
let value = f(shunt);
2207+
error.map(|()| value)
22902208
}
22912209

2292-
impl<I, T, E> Iterator for ResultShunt<I, E>
2210+
impl<I, T, E> Iterator for ResultShunt<'_, I, E>
22932211
where I: Iterator<Item = Result<T, E>>
22942212
{
22952213
type Item = T;
22962214

22972215
fn next(&mut self) -> Option<Self::Item> {
2298-
match self.iter.next() {
2299-
Some(Ok(v)) => Some(v),
2300-
Some(Err(e)) => {
2301-
self.error = Some(e);
2302-
None
2303-
}
2304-
None => None,
2305-
}
2216+
self.find(|_| true)
23062217
}
23072218

23082219
fn size_hint(&self) -> (usize, Option<usize>) {
2309-
if self.error.is_some() {
2220+
if self.error.is_err() {
23102221
(0, Some(0))
23112222
} else {
23122223
let (_, upper) = self.iter.size_hint();
23132224
(0, upper)
23142225
}
23152226
}
2227+
2228+
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
2229+
where
2230+
F: FnMut(B, Self::Item) -> R,
2231+
R: Try<Ok = B>,
2232+
{
2233+
let error = &mut *self.error;
2234+
self.iter
2235+
.try_fold(init, |acc, x| match x {
2236+
Ok(x) => LoopState::from_try(f(acc, x)),
2237+
Err(e) => {
2238+
*error = Err(e);
2239+
LoopState::Break(Try::from_ok(acc))
2240+
}
2241+
})
2242+
.into_try()
2243+
}
23162244
}

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
@@ -1203,6 +1203,23 @@ fn test_iterator_sum_result() {
12031203
assert_eq!(v.iter().cloned().sum::<Result<i32, _>>(), Ok(10));
12041204
let v: &[Result<i32, ()>] = &[Ok(1), Err(()), Ok(3), Ok(4)];
12051205
assert_eq!(v.iter().cloned().sum::<Result<i32, _>>(), Err(()));
1206+
1207+
#[derive(PartialEq, Debug)]
1208+
struct S(Result<i32, ()>);
1209+
1210+
impl Sum<Result<i32, ()>> for S {
1211+
fn sum<I: Iterator<Item = Result<i32, ()>>>(mut iter: I) -> Self {
1212+
// takes the sum by repeatedly calling `next` on `iter`,
1213+
// thus testing that repeated calls to `ResultShunt::try_fold`
1214+
// produce the expected results
1215+
Self(iter.by_ref().sum())
1216+
}
1217+
}
1218+
1219+
let v: &[Result<i32, ()>] = &[Ok(1), Ok(2), Ok(3), Ok(4)];
1220+
assert_eq!(v.iter().cloned().sum::<S>(), S(Ok(10)));
1221+
let v: &[Result<i32, ()>] = &[Ok(1), Err(()), Ok(3), Ok(4)];
1222+
assert_eq!(v.iter().cloned().sum::<S>(), S(Err(())));
12061223
}
12071224

12081225
#[test]

0 commit comments

Comments
 (0)