Skip to content

Future non-Send although non-Send local is dropped before .await #104883

Closed
@hniksic

Description

@hniksic

Rust 1.65 doesn't compile this code:

fn main() {
    tokio::spawn(async move {
        let non_send = get_non_send();
        drop(non_send);
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    });
}

fn get_non_send() -> Box<dyn std::io::Read> {
    unimplemented!()
}

Playground

It complains that the future is not Send, reporting the following:

error: future cannot be sent between threads safely
   --> src/main.rs:4:18
    |
4   |       tokio::spawn(async move {
    |  __________________^
5   | |         let non_send = get_non_send();
6   | |         drop(non_send);
7   | |         tokio::time::sleep(std::time::Duration::from_secs(1)).await;
8   | |     });
    | |_____^ future created by async block is not `Send`
    |
    = help: the trait `Send` is not implemented for `dyn std::io::Read`
note: future is not `Send` as this value is used across an await
   --> src/main.rs:7:62
    |
5   |         let non_send = get_non_send();
    |             -------- has type `Box<dyn std::io::Read>` which is not `Send`
6   |         drop(non_send);
7   |         tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    |                                                              ^^^^^^ await occurs here, with `non_send` maybe used later
8   |     });
    |     - `non_send` is later dropped here
note: required by a bound in `tokio::spawn`
   --> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.22.0/src/task/spawn.rs:163:21
    |
163 |         T: Future + Send + 'static,
    |                     ^^^^ required by this bound in `tokio::spawn`

I would expect it to compile, since the offending non-Send value is unconditionally dropped before .await.

If I change drop(non_send) to a block that makes it go out of scope, then it compiles:

    tokio::spawn(async move {
        {
            let non_send = get_non_send();
        }
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    });

Playground

As shown, the first example can be trivially transformed to use a scope, but in other situations it might not be as easy. This report is inspired by a post on reddit that described this issue in a slightly more complex scenario.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-async-awaitArea: Async & AwaitAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.C-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions