after reading the blog post about the async closures MVP I asked myself why there hadn't already been a crate in the past that made this possible? I used for example the async_fn_traits crate very often but it also doesn't work with closures.
I read also the RFC and it seems to me as if the main "trick" of the new feature is that the new traits AsyncFnMut and AsyncFn associate the lifetimes of the captures of the closure with that of the future. Wasn't it possible before to express that in your own code? Sure, closures have opaque types so you cannot explicitly mention them at a trait implementation but wasn't it possible before to "catch" them somehow with blanket implementations?
Note: I assume that it wasn't possible before, but I want to understand why.
Notice that Self::Output is a non-generic associated type, dependent only on the Args, and not on 'a. This means that the output of a Fn can borrow from its arguments, but it cannot borrow from self. You can't change this without using a different trait than Fn, which is exactly what the new AsyncFn trait is; you can see that there is a generic associated type for the future.
Demonstrating that async closures might not implement Fn:
#![feature(async_closure)]
fn accepts_fns<F, T>(f: F) where F: Fn() -> T {}
fn main() {
let captured = String::from("hello world");
accepts_fns(async move || {
println!("{captured}");
})
}
error: async closure does not implement `Fn` because it captures state from its environment
--> src/main.rs:7:17
|
7 | accepts_fns(async move || {
| ----------- ^^^^^^^^^^^^^
| |
| required by a bound introduced by this call
|
note: required by a bound in `accepts_fns`
--> src/main.rs:3:37
|
3 | fn accepts_fns<F, T>(f: F) where F: Fn() -> T {}
| ^^^^^^^^^ required by this bound in `accepts_fns`
On the other hand, if you remove the move, then the async closure does implement Fn, because it captures an &String, that can be copied into the future rather than borrowed from it as the String would be.
This is basically "lending Iterators but for functions", in case that clears anything up.[1]
For things like this... if the trait wasn't async specific that is. I haven't read enough to know if there's a good reason why the feature isn't/doesn't include an async agnostic version. âŠī¸