-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Tracking Issue for ops::Residual
(feature try_trait_v2_residual
)
#91285
Comments
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
@rustbot label +F-try_trait_v2 |
Now that GATs are stable, would it be possible to move the generic to the associated type? It would trait Residual {
type TryType<O>: Try<Output = O, Residual = Self>;
} // core::array
fn try_from_fn<T, const N: usize, R, F>(f: F) -> R::TryType<[T; N]>
where
R: Residual,
F: FnMut(usize) -> R::TryType<T>,
{ .. } // core::array
fn try_map<U, R, F>(self, f: F) -> R::TryType<[U; N]>
where
R: Residual,
F: FnMut(T) -> R::TryType<U>,
{ .. } |
Yeah, the current fn try_from_fn<R, const N: usize, F>(cb: F)
-> ChangeOutputType<R, [R::Output; N]>
where
F : FnMut(usize) -> R,
R : Try,
R::Residual : Residual<[R::Output; N]>, Notice that In a way, the type alias internally used by the stdlib already provides a way nicer name: So in this case, taking that name and using it on the trait itself, we would rather imagine: R::Residual : CanWrapOutputType<[R::Output; N]>,
Now we get something that does read better, although it talks a bit too much about type-level operations, which is something the stdlib doesn't do that often. So, rather than focusing on the how, if we focus on the what instead, we can stick to the "being a R::Residual : Residual, // <- Bonus: this wouldn't even be needed, since now we could eagerly add this bound to `Try`! and then using
I think it would be confusing to have some A technical remark, however, @h4x5: we can't use So, while keeping fn try_from_fn<T, const N: usize, F, Ret, Res>(f: F)
-> Res::TryTypeWithOutput<[T; N]>
where
F : FnMut(usize) -> Ret,
Ret : Try<Output = T, Residual = Res>,
Res : Residual, // EDIT
Or we could get rid of that fn try_from_fn<T, const N: usize, R, F>(f: F)
// -> <R::Residual as Residual>::TryTypeWithOutput<[T; N]>
-> ChangeOutputType<R, [T; N]>
where
F : FnMut(usize) -> R,
R : Try<Output = T>,
R::Residual : Residual, // EDIT |
Thanks! I'll try and draft a PR today to change it. Side note: |
Oh, good catch! (I've edited my post accordingly).
|
I've submitted #104128, an attempt at changing this. |
(See some additional conversation on zulip: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/lib.20after.20GATs/near/308206137) The thing I'm worried about using a GAT here is that it makes a bunch of things not work that would work without the GAT. The problem is that the GAT is the equivalent of a Imagine a Rust type for an #![feature(try_trait_v2_residual)]
#![feature(try_trait_v2)]
use std::marker::PhantomData;
use std::ops::{ControlFlow, Try, Residual, FromResidual};
mod hacks {
pub trait TypeThatWorksInHResult {
fn into_u32(self) -> u32;
fn from_u32(x: u32) -> Self;
}
impl TypeThatWorksInHResult for () {
fn into_u32(self) -> u32 { 0 }
fn from_u32(x: u32) { debug_assert!(x == 0); }
}
impl TypeThatWorksInHResult for bool {
// `S_FALSE` is `1`: <https://referencesource.microsoft.com/#windowsbase/Base/MS/Internal/Interop/ErrorCodes.cs,e08462dc3482f421>
fn into_u32(self) -> u32 { if self { 0 } else { 1 } }
fn from_u32(x: u32) -> bool { debug_assert!(x <= 1); x == 0 }
}
impl TypeThatWorksInHResult for u8 {
fn into_u32(self) -> u32 { self as _ }
fn from_u32(x: u32) -> u8 { debug_assert!(x <= 0xFF); x as _ }
}
impl TypeThatWorksInHResult for u16 {
fn into_u32(self) -> u32 { self as _ }
fn from_u32(x: u32) -> u16 { debug_assert!(x <= 0xFFFF); x as _ }
}
}
#[repr(transparent)]
pub struct HResult<T: hacks::TypeThatWorksInHResult>(u32, PhantomData<T>);
pub struct HResultResidual(u32); // TODO: use `NegativeI32`, once possible
impl<T: hacks::TypeThatWorksInHResult> Try for HResult<T> {
type Output = T;
type Residual = HResultResidual;
fn from_output(x: T) -> Self {
Self(x.into_u32(), PhantomData)
}
fn branch(self) -> ControlFlow<HResultResidual, T> {
if (self.0 as i32) < 0 {
ControlFlow::Break(HResultResidual(self.0))
} else {
ControlFlow::Continue(T::from_u32(self.0))
}
}
}
impl<T: hacks::TypeThatWorksInHResult> FromResidual for HResult<T> {
fn from_residual(r: HResultResidual) -> Self {
Self(r.0, PhantomData)
}
}
impl<T: hacks::TypeThatWorksInHResult> Residual<T> for HResultResidual {
type TryType = HResult<T>;
} But that Thoughts? |
Yeah. I was thinking about this more, and it did seem overly restrictive, but I couldn't think of a use case for For now, sticking with the non-GAT design seems like the best option. |
The #![feature(iterator_try_collect, array_try_from_fn)]
use std::{array, task::Poll};
fn main() {
let arr = [
Poll::Ready(Ok(3)),
Poll::Pending,
Poll::Ready(Ok(3)),
Poll::Ready(Err("NaN")),
Poll::Ready(Ok(4)),
];
// Why not `Poll<Result<Vec<i32>, &str>>`?
let _v: Result<Vec<Poll<i32>>, &str> = arr.into_iter().try_collect::<Vec<_>>();
// Why not `Poll<Result<[i32; 5], &str>>`?
let _arr: Result<[Poll<i32>; 5], &str> = array::try_from_fn(|i| arr[i]);
} It is because the |
Feature gate:
#![feature(try_trait_v2_residual)]
This is a tracking issue for the
ops::Residual
trait.This is used by
try_*
APIs that need to change from one member of a family to another, such astry_array_from_fn
#89379array::try_map
#79711For example, the closure passed to
Iterator::try_find
returnsFoo<bool>
, but the method wants to be able to returnFoo<Option<<Self as Iterator>::Item>>
.Public API
Steps / History
array::{try_from_fn, try_map}
andIterator::try_find
generic overTry
#91286Unresolved Questions
Try::Residual
is renamed (an open question in Tracking Issue fortry_trait_v2
, A new design for the?
desugaring (RFC#3058) #84277), this this probably should be too.The text was updated successfully, but these errors were encountered: