I am familiar with:
|x| { ... }
move |x| { ... }
I have tried
async move |x| { ... }
and gotten compiler error about experimental features.
What is:
|x| async move { ... }
?
I am familiar with:
|x| { ... }
move |x| { ... }
I have tried
async move |x| { ... }
and gotten compiler error about experimental features.
What is:
|x| async move { ... }
?
Refer to 2394-async_await - The Rust RFC Book where async || closures
is not implemented for now.
Also see 💤 Async closures - async fn fundamentals initiative .
Refer to async book: async/await - Asynchronous Programming in Rust .
async { ... }
is an async block, and async move { ... }
is an async block that captures variables by-value.
So |x| async move { ... }
is a closure that returns such an async block. You could also do move |x| async move { ... }
though with non-copy types, moving in the async block will also move into the closure anyways.
Should |x| async move { ... }
be interpreted as:
|x| {
/* do nothing */
async move { ... }
}
?
Yes. The closue returns an async block, and an async block is a constructor for a future. See "Async blocks vs async closures" in that RFC link.
I wonder if async closures are really needed (and for what), other than "consistency with async fn
".
One reason I see is specifying a return type (unless there is some other way that's not workaroundy?):
#![allow(unused_variables)]
#![feature(async_closure)]
#[tokio::main]
async fn main() {
// The non-async case:
let closure = |x: std::io::Result<String>| -> anyhow::Result<()> {
x?;
Ok(())
};
// `impl` not allowed here:
/*
let closure_async_impl = |x: std::io::Result<String>| -> impl std::future::Future<Output = anyhow::Result<()>> {
async {
x?;
Ok(())
}
};
*/
// On stable we must do:
let closure_async_turbofish = |x: std::io::Result<String>| async {
x?;
Ok::<_, anyhow::Error>(()) // workaround to specify the return type
};
// This is unstable, requires `feature(async_closure)`, and only works with `move`:
let async_closure = async move |x: std::io::Result<String>| -> anyhow::Result<()> {
x?;
Ok(())
};
}
The reason for async closure syntax is basically the same as the reason for async fn syntax, yes: to have different lifetime elision rules. Because the return type of closures are typically inferred rather than listed, this most often is a nonfunctional change.
I found this alternative way to specify a return type on stable:
fn main() {
let _closure_async_id = |x: std::io::Result<String>| async {
std::convert::identity::<anyhow::Result<()>>({
x?;
Ok(())
})
};
}
I have to re-read about closures and lifetimes. It's kinda confusing to me (yet).