Fixed size async machine; yet recursive function calls?

async claims to:

  1. not have a stack
  2. gets converted to fixed size state machines

yet async fns can call async fns / regular fns; how is this possible without a stack of some form ?

Async functions can't be directly recursive - this is a compile error:

pub async fn recursive() {
    recursive().await;
}
error[E0733]: recursion in an `async fn` requires boxing
 --> src/lib.rs:1:26
  |
1 | pub async fn recursive() {
  |                          ^ recursive `async fn`
  |
  = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
  = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion

Ah, so is the argument:

  1. we don't care about a stack of regular fns because the stack has to unwind to a fixed size before we hit an .await

  2. for async fns, they have to form a dag

therefore, although the stack can grow arbitrarily during the execution of an async fn, by the time it hits an .await, it is back to fixed size ?

Yes, except the requirement is even stronger: at point where you call await nothing is left on the stack.
That's the key to asynchronous execution: when async function is actually running β€” it borrows stack from the current thread and thus can use synchronous functions.
But at the point where you can β€œcall” await nothing is left on that stack, all the state is moved to pinned Future thus stack can be returned to Executor.
When Executor gives the async function a chance to continue (maybe on the same thread, maybe on the other thread) it lends stack again β€” but it must be freed before next await can be called.
This explanation should be enough for you to understand how and when you may call await and what's allowed and what's not allowed for async functions 99% of time.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.