Thought that async fn() and fn() -> impl Future<...> are the same, i.e both are async functions?
However, the it seems that the latter does not allow await calls.
Questions:
Are the two the same or equivalent?
Why is awaitnot allowed in fn do_async() ... ?
See sample code & error message below:
// is do_async() an async fn (because it returns a 'impl Future')?
fn do_async() -> impl Future<Output=u32> {
let _j = async { 5 }.await; // doesn't compile (error below) - Why??
async {10}
}
async fn do_another_async() -> u32 {
let _j = async { 5 }.await; // same as above but complies, not issues
10
}
Compilation Error here:
error[E0728]: `await` is only allowed inside `async` functions and blocks
|
22 | fn do_async() -> impl Future<Output=u32> {
| -------- this is not `async`
23 | let _j = async { 5 }.await; // doesn't compile - why?
| ^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks
fn fname(args…) -> impl 'args + Future<Output = Ret> {
/* return */ async move {
let _ = &args…; /* ensure all the args are captured */
body…
}
}
where 'args is a hidden generic lifetime parameter which represents the intersection of all the regions of code where each arg can be used, so that the returned `Future` is conservativley only usable there.
fn fname<'args, …> (
arg1: Arg1,
arg2: Arg2,
) -> impl 'args + Future<Output = Ret>
where
Arg1 : 'args,
Arg2 : 'args,
{
/* return */ async move {
let _ = &args…; /* ensure all the args are captured */
body…
}
}
You can try to use the showme feature of:
to have a proc-macro perform that unsugaring for you.
Relatedly, if you just want to be able to use -> impl Future… in the return type without having to wrap the whole function body within an async move { … }, you can use:
and it's #[bare_future] attribute (also named #[barefoot]):
use ::async_fn::prelude::*;
#[bare_future]
async fn do_async() -> impl Fut<'static, u32> /* + Send */ {
let _j = async { 5 }.await; // OK
10
}
(You can add the + Send there to have Rust ensure your future is thread-safe, which is very often required (In that regard, I cannot recommend enough that #![deny(clippy::future_not_send)] be used in most codebases).)
I don't understand how lifetimes affect this and the error doesn't complain about lifetimes, but says "await can only be used in async functions & block"
Isn't fn() -> impl Future<...> an async function?
In your example with bare_future, it's declared the same (with the preceding async) - this will make it equivalent to my async fn do_another_async() -> u32 which i know can compile with the await.
I am trying to avoid using 'async fn ...' in traits - i.e. need to use the async_trait crate and macro.
Your example showcased no lifetimes, but I just wanted to give the general answer.
The thing in Rust is that you can only use .await when inside an async context, which we could say ends up being an async [move] { … } block.
An async fn just happens to wrap its body in an implicit such block, whereas your "unsugared" code has no async { … } block around the .await you attempted to use.
In your case, the correct unsugaring would thus be:
Ok, i am confused and/or don't understand something fundamental here. Strangely, in the sample main() below, the ide/compiler/rust-checker? says a and b are the same type and i need to call await on them - so i still don't get (don't understand) what's the difference :
#[tokio::main]
async fn main() {
let a = do_another_async(); // variable a: impl Future<Output=u32>
let a = a.await;
let b = do_async(); // variable b: impl Future<Output=u32>
let b = b.await;
println!("a: {}; b: {}", a, b);
}
do_async is not an async fn, just a fn that returns a future. So you cannot use .await in its definition, unless you add an async block. But you can still .await its result inside an async function or block, because it still returns a future.
async is not "infectious" across functions. Just because you call do_async from inside an async fn does not mean that do_async itself becomes an async fn.
What async fn or async [move] {...} do for you is create a state machine, and the .await points inside the function or block define the transition points between states. This is why .await doesn't make sense outside of an async function or block: there's no state machine to affect.
Honestly, not really. I learned about async mostly by following the RFCs and reading the occasional blog post; I've never even used it in a non-trivial program. But the async book is probably a good place to start.