Skip to content

Change ResultShunt to be generic over Try #93572

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

Merged
merged 1 commit into from
Feb 8, 2022
Merged
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
78 changes: 38 additions & 40 deletions library/core/src/iter/adapters/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::iter::{InPlaceIterable, Iterator};
use crate::ops::{ControlFlow, Try};
use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, NeverShortCircuit, Residual, Try};

mod chain;
mod cloned;
Expand Down Expand Up @@ -128,59 +128,62 @@ pub unsafe trait SourceIter {
}

/// An iterator adapter that produces output as long as the underlying
/// iterator produces `Result::Ok` values.
/// iterator produces values where `Try::branch` says to `ControlFlow::Continue`.
///
/// If an error is encountered, the iterator stops and the error is
/// stored.
pub(crate) struct ResultShunt<'a, I, E> {
/// If a `ControlFlow::Break` is encountered, the iterator stops and the
/// residual is stored.
pub(crate) struct GenericShunt<'a, I, R> {
iter: I,
error: &'a mut Result<(), E>,
residual: &'a mut Option<R>,
}

/// Process the given iterator as if it yielded a `T` instead of a
/// `Result<T, _>`. Any errors will stop the inner iterator and
/// the overall result will be an error.
pub(crate) fn process_results<I, T, E, F, U>(iter: I, mut f: F) -> Result<U, E>
/// Process the given iterator as if it yielded a the item's `Try::Output`
/// type instead. Any `Try::Residual`s encountered will stop the inner iterator
/// and be propagated back to the overall result.
pub(crate) fn try_process<I, T, R, F, U>(iter: I, mut f: F) -> ChangeOutputType<I::Item, U>
where
I: Iterator<Item = Result<T, E>>,
for<'a> F: FnMut(ResultShunt<'a, I, E>) -> U,
I: Iterator<Item: Try<Output = T, Residual = R>>,
for<'a> F: FnMut(GenericShunt<'a, I, R>) -> U,
R: Residual<U>,
{
let mut error = Ok(());
let shunt = ResultShunt { iter, error: &mut error };
let mut residual = None;
let shunt = GenericShunt { iter, residual: &mut residual };
let value = f(shunt);
error.map(|()| value)
match residual {
Some(r) => FromResidual::from_residual(r),
None => Try::from_output(value),
}
}

impl<I, T, E> Iterator for ResultShunt<'_, I, E>
impl<I, R> Iterator for GenericShunt<'_, I, R>
where
I: Iterator<Item = Result<T, E>>,
I: Iterator<Item: Try<Residual = R>>,
{
type Item = T;
type Item = <I::Item as Try>::Output;

fn next(&mut self) -> Option<Self::Item> {
self.find(|_| true)
self.try_for_each(ControlFlow::Break).break_value()
}

fn size_hint(&self) -> (usize, Option<usize>) {
if self.error.is_err() {
if self.residual.is_some() {
(0, Some(0))
} else {
let (_, upper) = self.iter.size_hint();
(0, upper)
}
}

fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
fn try_fold<B, F, T>(&mut self, init: B, mut f: F) -> T
where
F: FnMut(B, Self::Item) -> R,
R: Try<Output = B>,
F: FnMut(B, Self::Item) -> T,
T: Try<Output = B>,
{
let error = &mut *self.error;
self.iter
.try_fold(init, |acc, x| match x {
Ok(x) => ControlFlow::from_try(f(acc, x)),
Err(e) => {
*error = Err(e);
.try_fold(init, |acc, x| match Try::branch(x) {
ControlFlow::Continue(x) => ControlFlow::from_try(f(acc, x)),
ControlFlow::Break(r) => {
*self.residual = Some(r);
ControlFlow::Break(try { acc })
}
})
Expand All @@ -192,17 +195,12 @@ where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
#[inline]
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
move |acc, x| Ok(f(acc, x))
}

self.try_fold(init, ok(fold)).unwrap()
self.try_fold(init, NeverShortCircuit::wrap_mut_2(fold)).0
}
}

#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, E> SourceIter for ResultShunt<'_, I, E>
unsafe impl<I, R> SourceIter for GenericShunt<'_, I, R>
where
I: SourceIter,
{
Expand All @@ -215,11 +213,11 @@ where
}
}

// SAFETY: ResultShunt::next calls I::find, which has to advance `iter` in order to
// return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's guaranteed that
// at least one item will be moved out from the underlying source.
// SAFETY: GenericShunt::next calls `I::try_for_each`, which has to advance `iter`
// in order to return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's
// guaranteed that at least one item will be moved out from the underlying source.
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, T, E> InPlaceIterable for ResultShunt<'_, I, E> where
I: Iterator<Item = Result<T, E>> + InPlaceIterable
unsafe impl<I, T, R> InPlaceIterable for GenericShunt<'_, I, R> where
I: Iterator<Item: Try<Output = T, Residual = R>> + InPlaceIterable
{
}
2 changes: 1 addition & 1 deletion library/core/src/iter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ pub use self::adapters::{
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
pub use self::adapters::{Intersperse, IntersperseWith};

pub(crate) use self::adapters::process_results;
pub(crate) use self::adapters::try_process;

mod adapters;
mod range;
Expand Down
8 changes: 4 additions & 4 deletions library/core/src/iter/traits/accum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ where
where
I: Iterator<Item = Result<U, E>>,
{
iter::process_results(iter, |i| i.sum())
iter::try_process(iter, |i| i.sum())
}
}

Expand All @@ -183,7 +183,7 @@ where
where
I: Iterator<Item = Result<U, E>>,
{
iter::process_results(iter, |i| i.product())
iter::try_process(iter, |i| i.product())
}
}

Expand All @@ -210,7 +210,7 @@ where
where
I: Iterator<Item = Option<U>>,
{
iter.map(|x| x.ok_or(())).sum::<Result<_, _>>().ok()
iter::try_process(iter, |i| i.sum())
}
}

Expand All @@ -226,6 +226,6 @@ where
where
I: Iterator<Item = Option<U>>,
{
iter.map(|x| x.ok_or(())).product::<Result<_, _>>().ok()
iter::try_process(iter, |i| i.product())
}
}
8 changes: 8 additions & 0 deletions library/core/src/ops/try_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,14 @@ pub(crate) type ChangeOutputType<T, V> = <<T as Try>::Residual as Residual<V>>::
#[repr(transparent)]
pub(crate) struct NeverShortCircuit<T>(pub T);

impl<T> NeverShortCircuit<T> {
/// Wrap a binary `FnMut` to return its result wrapped in a `NeverShortCircuit`.
#[inline]
pub fn wrap_mut_2<A, B>(mut f: impl FnMut(A, B) -> T) -> impl FnMut(A, B) -> Self {
move |a, b| NeverShortCircuit(f(a, b))
}
}

pub(crate) enum NeverShortCircuitResidual {}

impl<T> Try for NeverShortCircuit<T> {
Expand Down
4 changes: 2 additions & 2 deletions library/core/src/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@

#![stable(feature = "rust1", since = "1.0.0")]

use crate::iter::{FromIterator, FusedIterator, TrustedLen};
use crate::iter::{self, FromIterator, FusedIterator, TrustedLen};
use crate::panicking::{panic, panic_str};
use crate::pin::Pin;
use crate::{
Expand Down Expand Up @@ -2233,7 +2233,7 @@ impl<A, V: FromIterator<A>> FromIterator<Option<A>> for Option<V> {
// FIXME(#11084): This could be replaced with Iterator::scan when this
// performance bug is closed.

iter.into_iter().map(|x| x.ok_or(())).collect::<Result<_, _>>().ok()
iter::try_process(iter.into_iter(), |i| i.collect())
}
}

Expand Down
2 changes: 1 addition & 1 deletion library/core/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2016,7 +2016,7 @@ impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> {
// FIXME(#11084): This could be replaced with Iterator::scan when this
// performance bug is closed.

iter::process_results(iter.into_iter(), |i| i.collect())
iter::try_process(iter.into_iter(), |i| i.collect())
}
}

Expand Down