rust-lang / rust-lang/rust

Can't compile the use of `impl AsyncFn` within a spawned thread

Open
#138,207 1 comment 5 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-async-closures A-diagnostics D-confusing T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I'm getting the following errors when attempting to use a closure within a spawned thread, the errors are a bit too cryptic for me to understand how to correct the following code.

#[tokio::main]
async fn main() {
    tokio::spawn(async move {
        let text = String::from("");
        something(&text).await;
    });
}

async fn something(text: &String) {
    let try_something = async || {
        println!("{text}");
    };

    // try_something().await;   // works
    retry(try_something).await; // fails
}

async fn retry(action: impl AsyncFn()) {
    action().await;
}

Error:

error: implementation of `Send` is not general enough
 --> src/main.rs:3:5
  |
3 | /     tokio::spawn(async move {
4 | |         let text = String::from("");
5 | |         something(&text).await;
6 | |     });
  | |______^ implementation of `Send` is not general enough
  |
  = note: `Send` would have to be implemented for the type `&'0 &String`, for any lifetime `'0`...
  = note: ...but `Send` is actually implemented for the type `&'1 &String`, for some specific lifetime `'1`


Using impl Fn() -> impl Future instead of impl AsyncFn then works but for my use case, a mutable closure is needed which blocks me from using impl FnMut because of the following issue:

#[tokio::main]
async fn main() {
    let mut count = 0;

    let try_something = async || {
        count = 1;
    };

    retry(try_something).await;
}

async fn retry<F>(mut action: impl FnMut() -> F) // fails but would work if this was instead `impl AsyncFnMut()`
where
    F: Future<Output = ()>,
{
    action().await;
}

Error:

error: async closure does not implement `FnMut` because it captures state from its environment
  --> src/main.rs:26:25
   |
26 |     let try_something = async || {
   |                         ^^^^^^^^
...
30 |     retry(try_something).await;
   |     ----- required by a bound introduced by this call
   |
note: required by a bound in `retry`
  --> src/main.rs:33:36
   |
33 | async fn retry<F>(mut action: impl FnMut() -> F)
   |                                    ^^^^^^^^^^^^ required by this bound in `retry`


Essentially,

  1. I need to use a mutable reference in a closure which means I'd want to use async || {} instead of || async {},
  2. but using impl AsyncFnMut seems to break as soon as it's within a spawned thread which means I'd want to instead try: impl FnMut -> impl Future,
  3. but using impl FnMut seems to break the use of async || {} which means I'd want to instead try: || async {},
  4. but using || async {} no longer allows me to use a mutable reference which then breaks the required functionality
  5. and so I then repeat back to step 1, and I've been stuck on that loop for the last couple of hours, each time trying a different variation to try and get it to work but with no luck.

  • Is impl AsyncFn* not allowed within spawned threads?
  • Can async || {} not be passed to a function that takes impl FnMut -> impl Future?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the two minimal examples in src/main.rs, starting with the spawned AsyncFn case and then the mutable async-closure case. Inspect the emitted Send and FnMut diagnostics and determine whether the behavior is an implementation bug or a language limitation. Done means documenting the confirmed behavior with a focused regression test or a precise diagnostic improvement.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.