Skip to content

Commit d47a6cc

Browse files
committed
Auto merge of rust-lang#91286 - scottmcm:residual-trait, r=joshtriplett
Make `array::{try_from_fn, try_map}` and `Iterator::try_find` generic over `Try` Fixes rust-lang#85115 This only updates unstable functions. `array::try_map` didn't actually exist before; this adds it under the still-open tracking issue rust-lang#79711 from the old PR rust-lang#79713. Tracking issue for the new trait: rust-lang#91285 This would also solve the return type question in for the proposed `Iterator::try_reduce` in rust-lang#87054
2 parents 3e21768 + b96b9b4 commit d47a6cc

File tree

8 files changed

+195
-45
lines changed

8 files changed

+195
-45
lines changed

library/core/src/array/mod.rs

+84-32
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use crate::fmt;
1111
use crate::hash::{self, Hash};
1212
use crate::iter::TrustedLen;
1313
use crate::mem::{self, MaybeUninit};
14-
use crate::ops::{Index, IndexMut};
14+
use crate::ops::{
15+
ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try,
16+
};
1517
use crate::slice::{Iter, IterMut};
1618

1719
mod equality;
@@ -49,9 +51,13 @@ where
4951
}
5052

5153
/// Creates an array `[T; N]` where each fallible array element `T` is returned by the `cb` call.
52-
/// Unlike `core::array::from_fn`, where the element creation can't fail, this version will return an error
54+
/// Unlike [`from_fn`], where the element creation can't fail, this version will return an error
5355
/// if any element creation was unsuccessful.
5456
///
57+
/// The return type of this function depends on the return type of the closure.
58+
/// If you return `Result<T, E>` from the closure, you'll get a `Result<[T; N]; E>`.
59+
/// If you return `Option<T>` from the closure, you'll get an `Option<[T; N]>`.
60+
///
5561
/// # Arguments
5662
///
5763
/// * `cb`: Callback where the passed argument is the current array index.
@@ -60,27 +66,32 @@ where
6066
///
6167
/// ```rust
6268
/// #![feature(array_from_fn)]
69+
/// # // Apparently these doc tests are still on edition2018
70+
/// # use std::convert::TryInto;
6371
///
64-
/// #[derive(Debug, PartialEq)]
65-
/// enum SomeError {
66-
/// Foo,
67-
/// }
68-
///
69-
/// let array = core::array::try_from_fn(|i| Ok::<_, SomeError>(i));
72+
/// let array: Result<[u8; 5], _> = std::array::try_from_fn(|i| i.try_into());
7073
/// assert_eq!(array, Ok([0, 1, 2, 3, 4]));
7174
///
72-
/// let another_array = core::array::try_from_fn::<SomeError, _, (), 2>(|_| Err(SomeError::Foo));
73-
/// assert_eq!(another_array, Err(SomeError::Foo));
75+
/// let array: Result<[i8; 200], _> = std::array::try_from_fn(|i| i.try_into());
76+
/// assert!(array.is_err());
77+
///
78+
/// let array: Option<[_; 4]> = std::array::try_from_fn(|i| i.checked_add(100));
79+
/// assert_eq!(array, Some([100, 101, 102, 103]));
80+
///
81+
/// let array: Option<[_; 4]> = std::array::try_from_fn(|i| i.checked_sub(100));
82+
/// assert_eq!(array, None);
7483
/// ```
7584
#[inline]
7685
#[unstable(feature = "array_from_fn", issue = "89379")]
77-
pub fn try_from_fn<E, F, T, const N: usize>(cb: F) -> Result<[T; N], E>
86+
pub fn try_from_fn<F, R, const N: usize>(cb: F) -> ChangeOutputType<R, [R::Output; N]>
7887
where
79-
F: FnMut(usize) -> Result<T, E>,
88+
F: FnMut(usize) -> R,
89+
R: Try,
90+
R::Residual: Residual<[R::Output; N]>,
8091
{
8192
// SAFETY: we know for certain that this iterator will yield exactly `N`
8293
// items.
83-
unsafe { collect_into_array_rslt_unchecked(&mut (0..N).map(cb)) }
94+
unsafe { try_collect_into_array_unchecked(&mut (0..N).map(cb)) }
8495
}
8596

8697
/// Converts a reference to `T` into a reference to an array of length 1 (without copying).
@@ -444,6 +455,45 @@ impl<T, const N: usize> [T; N] {
444455
unsafe { collect_into_array_unchecked(&mut IntoIterator::into_iter(self).map(f)) }
445456
}
446457

458+
/// A fallible function `f` applied to each element on array `self` in order to
459+
/// return an array the same size as `self` or the first error encountered.
460+
///
461+
/// The return type of this function depends on the return type of the closure.
462+
/// If you return `Result<T, E>` from the closure, you'll get a `Result<[T; N]; E>`.
463+
/// If you return `Option<T>` from the closure, you'll get an `Option<[T; N]>`.
464+
///
465+
/// # Examples
466+
///
467+
/// ```
468+
/// #![feature(array_try_map)]
469+
/// let a = ["1", "2", "3"];
470+
/// let b = a.try_map(|v| v.parse::<u32>()).unwrap().map(|v| v + 1);
471+
/// assert_eq!(b, [2, 3, 4]);
472+
///
473+
/// let a = ["1", "2a", "3"];
474+
/// let b = a.try_map(|v| v.parse::<u32>());
475+
/// assert!(b.is_err());
476+
///
477+
/// use std::num::NonZeroU32;
478+
/// let z = [1, 2, 0, 3, 4];
479+
/// assert_eq!(z.try_map(NonZeroU32::new), None);
480+
/// let a = [1, 2, 3];
481+
/// let b = a.try_map(NonZeroU32::new);
482+
/// let c = b.map(|x| x.map(NonZeroU32::get));
483+
/// assert_eq!(c, Some(a));
484+
/// ```
485+
#[unstable(feature = "array_try_map", issue = "79711")]
486+
pub fn try_map<F, R>(self, f: F) -> ChangeOutputType<R, [R::Output; N]>
487+
where
488+
F: FnMut(T) -> R,
489+
R: Try,
490+
R::Residual: Residual<[R::Output; N]>,
491+
{
492+
// SAFETY: we know for certain that this iterator will yield exactly `N`
493+
// items.
494+
unsafe { try_collect_into_array_unchecked(&mut IntoIterator::into_iter(self).map(f)) }
495+
}
496+
447497
/// 'Zips up' two arrays into a single array of pairs.
448498
///
449499
/// `zip()` returns a new array where every element is a tuple where the
@@ -621,42 +671,42 @@ impl<T, const N: usize> [T; N] {
621671
/// Pulls `N` items from `iter` and returns them as an array. If the iterator
622672
/// yields fewer than `N` items, this function exhibits undefined behavior.
623673
///
624-
/// See [`collect_into_array`] for more information.
674+
/// See [`try_collect_into_array`] for more information.
625675
///
626676
///
627677
/// # Safety
628678
///
629679
/// It is up to the caller to guarantee that `iter` yields at least `N` items.
630680
/// Violating this condition causes undefined behavior.
631-
unsafe fn collect_into_array_rslt_unchecked<E, I, T, const N: usize>(
632-
iter: &mut I,
633-
) -> Result<[T; N], E>
681+
unsafe fn try_collect_into_array_unchecked<I, T, R, const N: usize>(iter: &mut I) -> R::TryType
634682
where
635683
// Note: `TrustedLen` here is somewhat of an experiment. This is just an
636684
// internal function, so feel free to remove if this bound turns out to be a
637685
// bad idea. In that case, remember to also remove the lower bound
638686
// `debug_assert!` below!
639-
I: Iterator<Item = Result<T, E>> + TrustedLen,
687+
I: Iterator + TrustedLen,
688+
I::Item: Try<Output = T, Residual = R>,
689+
R: Residual<[T; N]>,
640690
{
641691
debug_assert!(N <= iter.size_hint().1.unwrap_or(usize::MAX));
642692
debug_assert!(N <= iter.size_hint().0);
643693

644694
// SAFETY: covered by the function contract.
645-
unsafe { collect_into_array(iter).unwrap_unchecked() }
695+
unsafe { try_collect_into_array(iter).unwrap_unchecked() }
646696
}
647697

648-
// Infallible version of `collect_into_array_rslt_unchecked`.
698+
// Infallible version of `try_collect_into_array_unchecked`.
649699
unsafe fn collect_into_array_unchecked<I, const N: usize>(iter: &mut I) -> [I::Item; N]
650700
where
651701
I: Iterator + TrustedLen,
652702
{
653-
let mut map = iter.map(Ok::<_, Infallible>);
703+
let mut map = iter.map(NeverShortCircuit);
654704

655705
// SAFETY: The same safety considerations w.r.t. the iterator length
656-
// apply for `collect_into_array_rslt_unchecked` as for
706+
// apply for `try_collect_into_array_unchecked` as for
657707
// `collect_into_array_unchecked`
658-
match unsafe { collect_into_array_rslt_unchecked(&mut map) } {
659-
Ok(array) => array,
708+
match unsafe { try_collect_into_array_unchecked(&mut map) } {
709+
NeverShortCircuit(array) => array,
660710
}
661711
}
662712

@@ -670,13 +720,15 @@ where
670720
///
671721
/// If `iter.next()` panicks, all items already yielded by the iterator are
672722
/// dropped.
673-
fn collect_into_array<E, I, T, const N: usize>(iter: &mut I) -> Option<Result<[T; N], E>>
723+
fn try_collect_into_array<I, T, R, const N: usize>(iter: &mut I) -> Option<R::TryType>
674724
where
675-
I: Iterator<Item = Result<T, E>>,
725+
I: Iterator,
726+
I::Item: Try<Output = T, Residual = R>,
727+
R: Residual<[T; N]>,
676728
{
677729
if N == 0 {
678730
// SAFETY: An empty array is always inhabited and has no validity invariants.
679-
return unsafe { Some(Ok(mem::zeroed())) };
731+
return unsafe { Some(Try::from_output(mem::zeroed())) };
680732
}
681733

682734
struct Guard<'a, T, const N: usize> {
@@ -701,11 +753,11 @@ where
701753
let mut guard = Guard { array_mut: &mut array, initialized: 0 };
702754

703755
while let Some(item_rslt) = iter.next() {
704-
let item = match item_rslt {
705-
Err(err) => {
706-
return Some(Err(err));
756+
let item = match item_rslt.branch() {
757+
ControlFlow::Break(r) => {
758+
return Some(FromResidual::from_residual(r));
707759
}
708-
Ok(elem) => elem,
760+
ControlFlow::Continue(elem) => elem,
709761
};
710762

711763
// SAFETY: `guard.initialized` starts at 0, is increased by one in the
@@ -723,7 +775,7 @@ where
723775
// SAFETY: the condition above asserts that all elements are
724776
// initialized.
725777
let out = unsafe { MaybeUninit::array_assume_init(array) };
726-
return Some(Ok(out));
778+
return Some(Try::from_output(out));
727779
}
728780
}
729781

library/core/src/iter/traits/iterator.rs

+32-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::cmp::{self, Ordering};
2-
use crate::ops::{ControlFlow, Try};
2+
use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try};
33

44
use super::super::TrustedRandomAccessNoCoerce;
55
use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse};
@@ -2418,6 +2418,10 @@ pub trait Iterator {
24182418
/// Applies function to the elements of iterator and returns
24192419
/// the first true result or the first error.
24202420
///
2421+
/// The return type of this method depends on the return type of the closure.
2422+
/// If you return `Result<bool, E>` from the closure, you'll get a `Result<Option<Self::Item>; E>`.
2423+
/// If you return `Option<bool>` from the closure, you'll get an `Option<Option<Self::Item>>`.
2424+
///
24212425
/// # Examples
24222426
///
24232427
/// ```
@@ -2435,32 +2439,48 @@ pub trait Iterator {
24352439
/// let result = a.iter().try_find(|&&s| is_my_num(s, 5));
24362440
/// assert!(result.is_err());
24372441
/// ```
2442+
///
2443+
/// This also supports other types which implement `Try`, not just `Result`.
2444+
/// ```
2445+
/// #![feature(try_find)]
2446+
///
2447+
/// use std::num::NonZeroU32;
2448+
/// let a = [3, 5, 7, 4, 9, 0, 11];
2449+
/// let result = a.iter().try_find(|&&x| NonZeroU32::new(x).map(|y| y.is_power_of_two()));
2450+
/// assert_eq!(result, Some(Some(&4)));
2451+
/// let result = a.iter().take(3).try_find(|&&x| NonZeroU32::new(x).map(|y| y.is_power_of_two()));
2452+
/// assert_eq!(result, Some(None));
2453+
/// let result = a.iter().rev().try_find(|&&x| NonZeroU32::new(x).map(|y| y.is_power_of_two()));
2454+
/// assert_eq!(result, None);
2455+
/// ```
24382456
#[inline]
24392457
#[unstable(feature = "try_find", reason = "new API", issue = "63178")]
2440-
fn try_find<F, R, E>(&mut self, f: F) -> Result<Option<Self::Item>, E>
2458+
fn try_find<F, R>(&mut self, f: F) -> ChangeOutputType<R, Option<Self::Item>>
24412459
where
24422460
Self: Sized,
24432461
F: FnMut(&Self::Item) -> R,
24442462
R: Try<Output = bool>,
2445-
// FIXME: This bound is rather strange, but means minimal breakage on nightly.
2446-
// See #85115 for the issue tracking a holistic solution for this and try_map.
2447-
R: Try<Residual = Result<crate::convert::Infallible, E>>,
2463+
R::Residual: Residual<Option<Self::Item>>,
24482464
{
24492465
#[inline]
2450-
fn check<F, T, R, E>(mut f: F) -> impl FnMut((), T) -> ControlFlow<Result<T, E>>
2466+
fn check<I, V, R>(
2467+
mut f: impl FnMut(&I) -> V,
2468+
) -> impl FnMut((), I) -> ControlFlow<R::TryType>
24512469
where
2452-
F: FnMut(&T) -> R,
2453-
R: Try<Output = bool>,
2454-
R: Try<Residual = Result<crate::convert::Infallible, E>>,
2470+
V: Try<Output = bool, Residual = R>,
2471+
R: Residual<Option<I>>,
24552472
{
24562473
move |(), x| match f(&x).branch() {
24572474
ControlFlow::Continue(false) => ControlFlow::CONTINUE,
2458-
ControlFlow::Continue(true) => ControlFlow::Break(Ok(x)),
2459-
ControlFlow::Break(Err(x)) => ControlFlow::Break(Err(x)),
2475+
ControlFlow::Continue(true) => ControlFlow::Break(Try::from_output(Some(x))),
2476+
ControlFlow::Break(r) => ControlFlow::Break(FromResidual::from_residual(r)),
24602477
}
24612478
}
24622479

2463-
self.try_fold((), check(f)).break_value().transpose()
2480+
match self.try_fold((), check(f)) {
2481+
ControlFlow::Break(x) => x,
2482+
ControlFlow::Continue(()) => Try::from_output(None),
2483+
}
24642484
}
24652485

24662486
/// Searches for an element in an iterator, returning its index.

library/core/src/ops/control_flow.rs

+5
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ impl<B, C> ops::FromResidual for ControlFlow<B, C> {
123123
}
124124
}
125125

126+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
127+
impl<B, C> ops::Residual<C> for ControlFlow<B, convert::Infallible> {
128+
type TryType = ControlFlow<B, C>;
129+
}
130+
126131
impl<B, C> ControlFlow<B, C> {
127132
/// Returns `true` if this is a `Break` variant.
128133
///

library/core/src/ops/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ pub use self::range::OneSidedRange;
187187
#[unstable(feature = "try_trait_v2", issue = "84277")]
188188
pub use self::try_trait::{FromResidual, Try};
189189

190+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
191+
pub use self::try_trait::Residual;
192+
193+
pub(crate) use self::try_trait::{ChangeOutputType, NeverShortCircuit};
194+
190195
#[unstable(feature = "generator_trait", issue = "43122")]
191196
pub use self::generator::{Generator, GeneratorState};
192197

library/core/src/ops/try_trait.rs

+58
Original file line numberDiff line numberDiff line change
@@ -338,3 +338,61 @@ pub trait FromResidual<R = <Self as Try>::Residual> {
338338
#[unstable(feature = "try_trait_v2", issue = "84277")]
339339
fn from_residual(residual: R) -> Self;
340340
}
341+
342+
/// Allows retrieving the canonical type implementing [`Try`] that has this type
343+
/// as its residual and allows it to hold an `O` as its output.
344+
///
345+
/// If you think of the `Try` trait as splitting a type into its [`Try::Output`]
346+
/// and [`Try::Residual`] components, this allows putting them back together.
347+
///
348+
/// For example,
349+
/// `Result<T, E>: Try<Output = T, Residual = Result<Infallible, E>>`,
350+
/// and in the other direction,
351+
/// `<Result<Infallible, E> as Residual<T>>::TryType = Result<T, E>`.
352+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
353+
pub trait Residual<O> {
354+
/// The "return" type of this meta-function.
355+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
356+
type TryType: Try<Output = O, Residual = Self>;
357+
}
358+
359+
#[unstable(feature = "pub_crate_should_not_need_unstable_attr", issue = "none")]
360+
pub(crate) type ChangeOutputType<T, V> = <<T as Try>::Residual as Residual<V>>::TryType;
361+
362+
/// An adapter for implementing non-try methods via the `Try` implementation.
363+
///
364+
/// Conceptually the same as `Result<T, !>`, but requiring less work in trait
365+
/// solving and inhabited-ness checking and such, by being an obvious newtype
366+
/// and not having `From` bounds lying around.
367+
///
368+
/// Not currently planned to be exposed publicly, so just `pub(crate)`.
369+
#[repr(transparent)]
370+
pub(crate) struct NeverShortCircuit<T>(pub T);
371+
372+
pub(crate) enum NeverShortCircuitResidual {}
373+
374+
impl<T> Try for NeverShortCircuit<T> {
375+
type Output = T;
376+
type Residual = NeverShortCircuitResidual;
377+
378+
#[inline]
379+
fn branch(self) -> ControlFlow<NeverShortCircuitResidual, T> {
380+
ControlFlow::Continue(self.0)
381+
}
382+
383+
#[inline]
384+
fn from_output(x: T) -> Self {
385+
NeverShortCircuit(x)
386+
}
387+
}
388+
389+
impl<T> FromResidual for NeverShortCircuit<T> {
390+
#[inline]
391+
fn from_residual(never: NeverShortCircuitResidual) -> Self {
392+
match never {}
393+
}
394+
}
395+
396+
impl<T> Residual<T> for NeverShortCircuitResidual {
397+
type TryType = NeverShortCircuit<T>;
398+
}

library/core/src/option.rs

+5
Original file line numberDiff line numberDiff line change
@@ -2089,6 +2089,11 @@ impl<T> const ops::FromResidual for Option<T> {
20892089
}
20902090
}
20912091

2092+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
2093+
impl<T> ops::Residual<T> for Option<convert::Infallible> {
2094+
type TryType = Option<T>;
2095+
}
2096+
20922097
impl<T> Option<Option<T>> {
20932098
/// Converts from `Option<Option<T>>` to `Option<T>`.
20942099
///

library/core/src/result.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1959,3 +1959,8 @@ impl<T, E, F: From<E>> ops::FromResidual<Result<convert::Infallible, E>> for Res
19591959
}
19601960
}
19611961
}
1962+
1963+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
1964+
impl<T, E> ops::Residual<T> for Result<convert::Infallible, E> {
1965+
type TryType = Result<T, E>;
1966+
}

0 commit comments

Comments
 (0)