rust-lang / rust-lang/rust

Misleading help suggests `Sync` bound when shareable reference is passed across or into await

Open
#129,105 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

#59245 results in an error that may be difficult to interpret when structuring generic async code:

use std::fmt::Display;

async fn run(mut state: impl Display) {
    do_stuff(&state).await;
    // ...
}

async fn do_stuff(state: &impl Display) {
    println!("{state}");
}

fn spawn_task<T>(state: T)
where
    T: Display + Send + 'static,
{
    tokio::spawn(run(state));
}

The compiler (as of 1.82.0-nightly (80eb5a8e9 2024-08-13)) produces this error output:

error[E0277]: `T` cannot be shared between threads safely
   --> src/main.rs:16:18
    |
16  |     tokio::spawn(run(state));
    |     ------------ ^^^^^^^^^^ `T` cannot be shared between threads safely
    |     |
    |     required by a bound introduced by this call
    |
    = note: required for `&T` to implement `Send`
note: required because it's used within this `async` fn body
   --> src/main.rs:8:41
    |
8   |   async fn do_stuff(state: &impl Display) {
    |  _________________________________________^
9   | |     println!("{state}");
10  | | }
    | |_^
note: required because it's used within this `async` fn body
   --> src/main.rs:3:35
    |
3   |   async fn run(state: impl Display) {
    |  ___________________________________^
4   | |     do_stuff(&state).await;
5   | |     // ...
6   | | }
    | |_^
note: required by a bound in `tokio::spawn`
   --> /home/mzabaluev/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.2/src/task/spawn.rs:167:21
    |
165 |     pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
    |            ----- required by a bound in this function
166 |     where
167 |         F: Future + Send + 'static,
    |                     ^^^^ required by this bound in `spawn`
help: consider further restricting this bound
    |
14  |     T: Display + Send + 'static + std::marker::Sync,
    |                                 +++++++++++++++++++

A non-restrictive, but also unintuitive, solution is to make the reference passed to do_stuff mutable (i.e. provably exclusive), even though mutability is not required by the function body.

Desired outcome

The help heuristic should detect that the Sync bound arises due to a shareable reference becoming a member of an async closure for which Send is required, and suggest using an exclusive reference as an alternative to restricting the bound.

Originally posted by @mzabaluev in https://github.com/rust-lang/rust/issues/59245#issuecomment-2289520996

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 E0277 diagnostic from the example and inspect the compiler's help heuristic for the Sync suggestion when a shareable reference is held across an await. Trace how the async body and required Send bound are reported; done means the diagnostic offers an exclusive-reference alternative instead of only suggesting Sync.

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
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.