You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Oct 30, 2019. It is now read-only.
Repository navigation
This repository was archived by the owner on Oct 30, 2019. It is now read-only.
Async closures with borrowed arguments are a messy thing. Since the output type of the closure (impl Future + 'a) depends upon the input lifetime of the argument, these closures are always higher-ranked. Eventually I think we'd like to support this through syntax like this:
Unfortunately, the current impl Trait-in-args implementation doesn't support these sorts of higher-ranked use cases. Luckily it's not backwards incompatible to add support for them, since we've currently banned impl Trait inside of Fn syntax.
Until we get one of these nicer solutions, however, we need a way to type these sorts of functions. I made an attempt at one of them that looks like this:
Unfortunately, this fails because of rust-lang/rust#51004. Even if this worked, though, this is an awful lot of code to write for each of these functions-- we'd likely want some sort of macro.
Until progress is made on this issue, it's impossible to make a function which accepts an asynchronous closure with an argument borrowed for a non-fixed lifetime (precise named lifetimes like impl FnOnce(&'tcx Foo) -> F are possible).
Just to tease out the different threads of this, let me know if anything about this isn't correct:
impl Fn(&u8) doesn't do the HRTB elision that F: Fn(&u8) does today.
In general, writing the signature of an async function is a pain, made worse by the first point. It would be nice to have some sugar for the signature of an async function, just as we have sugar for declaring one.
You have a handwritten solution, which also doesn't work for type system reason (and which you'd want to macro generate).
Am I misunderstanding any of this?
It seems like we should solve 1 promptly (what blockers are there?) and 2 less promptly (since it involves design work). Pursuing the very complicated trait wrapper thing too far seems like a misfocus to me.
Nevermind, I think I have a better understanding of the issue from investigating this comment:
Until progress is made on this issue, it's impossible to make a function which accepts an asynchronous closure with an argument borrowed for a non-fixed lifetime
The problem seems to be mainly that you can't write F: FnOnce() -> impl Future, and there's no other way to tie the lifetimes together than to do that, because you want to write for<'a> F: FnOnce() -> Fut, Fut: 'a
@Nemo157 That gives you a function which can borrow a particular'a, not an arbitrary 'a. You'd then have to manufacture a reference with that particular lifetime, which is impossible in many cases (often the borrowed value you're passing in would be of an anonymous lifetime defined by a scope of a particular function or lifetime of a particular borrow of a value-- see rust-lang/futures-rs#1023 for an example).
But this is rejected due to recursive trait inheritance. Instead, we can say Fn implies AsyncFn by blanket impls but not the other way around (by lack of trait inheritance). Also, we can split them by the number of arguments to bring this trait in stable Rust.
The callee has to write f.call3(a, b, c) instead of f(a, b, c). For the caller, however, this looks like a usual higher-order async fn which receives async fn or async closure.
Activity
withoutboats commentedon Jun 19, 2018
Just to tease out the different threads of this, let me know if anything about this isn't correct:
impl Fn(&u8)doesn't do the HRTB elision thatF: Fn(&u8)does today.Am I misunderstanding any of this?
It seems like we should solve 1 promptly (what blockers are there?) and 2 less promptly (since it involves design work). Pursuing the very complicated trait wrapper thing too far seems like a misfocus to me.
withoutboats commentedon Jun 19, 2018
Nevermind, I think I have a better understanding of the issue from investigating this comment:
The problem seems to be mainly that you can't write
F: FnOnce() -> impl Future, and there's no other way to tie the lifetimes together than to do that, because you want to writefor<'a> F: FnOnce() -> Fut, Fut: 'aNemo157 commentedon Jun 20, 2018
Is there any reason fully expanding the lifetime and closure return type out to generic type parameters doesn't work?
This is how I was previously assuming higher order anonymous universal types would be desugared.
cramertj commentedon Jun 20, 2018
@Nemo157 That gives you a function which can borrow a particular
'a, not an arbitrary'a. You'd then have to manufacture a reference with that particular lifetime, which is impossible in many cases (often the borrowed value you're passing in would be of an anonymous lifetime defined by a scope of a particular function or lifetime of a particular borrow of a value-- see rust-lang/futures-rs#1023 for an example).qnighy commentedon Jul 25, 2019
Coming from this URLO thread. I propose this workaround:
The
AsyncFnhere philosophically inheritsFnin this way:But this is rejected due to recursive trait inheritance. Instead, we can say
FnimpliesAsyncFnby blanket impls but not the other way around (by lack of trait inheritance). Also, we can split them by the number of arguments to bring this trait in stable Rust.The callee has to write
f.call3(a, b, c)instead off(a, b, c). For the caller, however, this looks like a usual higher-order async fn which receives async fn or async closure.