From 413945ecc5eba7a76c9e73c85806a0405c53a94b Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 1 Feb 2022 21:53:30 -0800 Subject: [PATCH] Change `ResultShunt` to be generic over `Try` Just a refactor (and rename) for now, so it's not `Result`-specific. This could be used for a future `Iterator::try_collect`, or similar, but anything like that is left for a future PR. --- library/core/src/iter/adapters/mod.rs | 78 +++++++++++++-------------- library/core/src/iter/mod.rs | 2 +- library/core/src/iter/traits/accum.rs | 8 +-- library/core/src/ops/try_trait.rs | 8 +++ library/core/src/option.rs | 4 +- library/core/src/result.rs | 2 +- 6 files changed, 54 insertions(+), 48 deletions(-) diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index db8776ac7418d..2ae92e89d63b5 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -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; @@ -128,41 +128,45 @@ 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, } -/// Process the given iterator as if it yielded a `T` instead of a -/// `Result`. Any errors will stop the inner iterator and -/// the overall result will be an error. -pub(crate) fn process_results(iter: I, mut f: F) -> Result +/// 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(iter: I, mut f: F) -> ChangeOutputType where - I: Iterator>, - for<'a> F: FnMut(ResultShunt<'a, I, E>) -> U, + I: Iterator>, + for<'a> F: FnMut(GenericShunt<'a, I, R>) -> U, + R: Residual, { - 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 Iterator for ResultShunt<'_, I, E> +impl Iterator for GenericShunt<'_, I, R> where - I: Iterator>, + I: Iterator>, { - type Item = T; + type Item = ::Output; fn next(&mut self) -> Option { - self.find(|_| true) + self.try_for_each(ControlFlow::Break).break_value() } fn size_hint(&self) -> (usize, Option) { - if self.error.is_err() { + if self.residual.is_some() { (0, Some(0)) } else { let (_, upper) = self.iter.size_hint(); @@ -170,17 +174,16 @@ where } } - fn try_fold(&mut self, init: B, mut f: F) -> R + fn try_fold(&mut self, init: B, mut f: F) -> T where - F: FnMut(B, Self::Item) -> R, - R: Try, + F: FnMut(B, Self::Item) -> T, + T: Try, { - 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 }) } }) @@ -192,17 +195,12 @@ where Self: Sized, F: FnMut(B, Self::Item) -> B, { - #[inline] - fn ok(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result { - 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 SourceIter for ResultShunt<'_, I, E> +unsafe impl SourceIter for GenericShunt<'_, I, R> where I: SourceIter, { @@ -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 InPlaceIterable for ResultShunt<'_, I, E> where - I: Iterator> + InPlaceIterable +unsafe impl InPlaceIterable for GenericShunt<'_, I, R> where + I: Iterator> + InPlaceIterable { } diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index da459ed7c68f4..65f56f64dbfa6 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -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; diff --git a/library/core/src/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs index c2e837df5ff2a..84d83ee39699f 100644 --- a/library/core/src/iter/traits/accum.rs +++ b/library/core/src/iter/traits/accum.rs @@ -167,7 +167,7 @@ where where I: Iterator>, { - iter::process_results(iter, |i| i.sum()) + iter::try_process(iter, |i| i.sum()) } } @@ -183,7 +183,7 @@ where where I: Iterator>, { - iter::process_results(iter, |i| i.product()) + iter::try_process(iter, |i| i.product()) } } @@ -210,7 +210,7 @@ where where I: Iterator>, { - iter.map(|x| x.ok_or(())).sum::>().ok() + iter::try_process(iter, |i| i.sum()) } } @@ -226,6 +226,6 @@ where where I: Iterator>, { - iter.map(|x| x.ok_or(())).product::>().ok() + iter::try_process(iter, |i| i.product()) } } diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 6a414ae8c4b80..eac426ad31164 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -359,6 +359,14 @@ pub(crate) type ChangeOutputType = <::Residual as Residual>:: #[repr(transparent)] pub(crate) struct NeverShortCircuit(pub T); +impl NeverShortCircuit { + /// Wrap a binary `FnMut` to return its result wrapped in a `NeverShortCircuit`. + #[inline] + pub fn wrap_mut_2(mut f: impl FnMut(A, B) -> T) -> impl FnMut(A, B) -> Self { + move |a, b| NeverShortCircuit(f(a, b)) + } +} + pub(crate) enum NeverShortCircuitResidual {} impl Try for NeverShortCircuit { diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 611f4ab38ab33..ec04692d3e0c8 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -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::{ @@ -2233,7 +2233,7 @@ impl> FromIterator> for Option { // FIXME(#11084): This could be replaced with Iterator::scan when this // performance bug is closed. - iter.into_iter().map(|x| x.ok_or(())).collect::>().ok() + iter::try_process(iter.into_iter(), |i| i.collect()) } } diff --git a/library/core/src/result.rs b/library/core/src/result.rs index fbd6d419236ae..05b4fa035b1ae 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -2016,7 +2016,7 @@ impl> FromIterator> for Result { // 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()) } }