Skip to content

async fn cannot be recursive #53690

Closed
Closed
@upsuper

Description

@upsuper

With the current design of async functions, it doesn't seem to be able to call them recursively.

The most straightforward way:

async fn foo() -> u32 {
    await!(foo()) + 1
}

fails apparently, because the size of the future type is indefinite, and thus the compiler complains:

error[E0275]: overflow evaluating the requirement `impl std::future::Future`
  |
  = help: consider adding a `#![recursion_limit="128"]` attribute to your crate

Another idea would be to put the recursive future into another heap-allocated object:

async fn foo() -> u32 {
    let x = Box::new(foo());
    await!(x) + 1
}

However this doesn't work either, because resolving the return type of foo requires the return type of foo, which forms a cycle dependency, and compiler complains:

error[E0391]: cycle detected when processing `foo`
 --> src/main.rs:7:1
  |
7 | async fn foo() -> u32 {
  | ^^^^^^^^^^^^^^^^^^^^^
  |
note: ...which requires evaluating trait selection obligation `std::boxed::Box<impl std::future::Future>: std::future::Future`...
note: ...which requires processing `foo::{{impl-Trait}}`...

If the type is a problem, maybe we can use trait object?

async fn foo() -> u32 {
    let x: Box<dyn Future<Output = u32>> = Box::new(foo());
    await!(x) + 1
}

But it still doesn't work, because Future isn't object-safe due to the poll method, which is correctly reported by the compiler:

error[E0038]: the trait `std::future::Future` cannot be made into an object
  --> src/main.rs:10:12
   |
10 |     let x: Box<dyn Future<Output = u32>> = Box::new(foo());
   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::future::Future` cannot be made into an object
   |
   = note: method `poll` has a non-standard `self` type

So it seems that there is no way an async function can be called recursively.

I'm not sure how much problematic it would be, but it seems this limitation wasn't mentioned in the RFC nor any introduction of async, so maybe it's worth considering.

Recursion without additional allocation may be very challenging, but we should probably allow opt-in async recursion with some explicit cost.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-impl-traitArea: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions