Skip to content

rustc fails to prove Sendness for an async block where a !Send value is dropped before an .await point #128095

@frank-king

Description

@frank-king

I tried this code: (playground)

#[tokio::main] async fn main() { let (tx, rx) = tokio::sync::watch::channel(vec![1, 1, 4, 5, 1, 4]); let mut binding = rx.clone(); tokio::spawn(async move { let value = binding.borrow(); let len = value.len(); drop(value); println!("len: {}", len); binding.changed().await .expect("watch error"); }).await; println!("{:?}", rx.borrow()); }

I expected to see this happen: the code passes compilation.

Instead, this happened:

error: future cannot be sent between threads safely --> src/main.rs:5:5 | 5 | / tokio::spawn(async move { 6 | | let value = binding.borrow(); 7 | | let len = value.len(); 8 | | drop(value); ... | 11 | | .expect("watch error"); 12 | | }).await; | |______^ future created by async block is not `Send` | = help: within `{async block@src/main.rs:5:18: 5:28}`, the trait `Send` is not implemented for `std::sync::RwLockReadGuard<'_, Vec<i32>>`, which is required by `{async block@src/main.rs:5:18: 5:28}: Send` note: future is not `Send` as this value is used across an await --> src/main.rs:10:27 | 6 | let value = binding.borrow(); | ----- has type `tokio::sync::watch::Ref<'_, Vec<i32>>` which is not `Send` ... 10 | binding.changed().await | ^^^^^ await occurs here, with `value` maybe used later note: required by a bound in `tokio::spawn` --> /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/task/spawn.rs:166:21 | 164 | pub fn spawn<F>(future: F) -> JoinHandle<F::Output> | ----- required by a bound in this function 165 | where 166 | F: Future + Send + 'static, | ^^^^ required by this bound in `spawn` error: future cannot be sent between threads safely --> src/main.rs:5:5 | 5 | / tokio::spawn(async move { 6 | | let value = binding.borrow(); 7 | | let len = value.len(); 8 | | drop(value); ... | 11 | | .expect("watch error"); 12 | | }).await; | |______^ future created by async block is not `Send` | = help: within `{async block@src/main.rs:5:18: 5:28}`, the trait `Send` is not implemented for `*mut ()`, which is required by `{async block@src/main.rs:5:18: 5:28}: Send` note: future is not `Send` as this value is used across an await --> src/main.rs:10:27 | 6 | let value = binding.borrow(); | ----- has type `tokio::sync::watch::Ref<'_, Vec<i32>>` which is not `Send` ... 10 | binding.changed().await | ^^^^^ await occurs here, with `value` maybe used later note: required by a bound in `tokio::spawn` --> /playground/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.38.0/src/task/spawn.rs:166:21 | 164 | pub fn spawn<F>(future: F) -> JoinHandle<F::Output> | ----- required by a bound in this function 165 | where 166 | F: Future + Send + 'static, | ^^^^ required by this bound in `spawn` 

However, this code works fine: (playground)

#[tokio::main] async fn main() { let (tx, rx) = tokio::sync::watch::channel(vec![1, 1, 4, 5, 1, 4]); let mut binding = rx.clone(); tokio::spawn(async move { let len = { let value = binding.borrow(); value.len() }; println!("len: {}", len); binding.changed().await .expect("watch error"); }).await; println!("{:?}", rx.borrow()); }

Meta: nightly rust (1.81.0-nightly 2024-07-12) and stable rust (1.79.0) behave similarly.

I have constructed a minimum reproducible version:

#![allow(unused)] struct NotSend(std::marker::PhantomData<*mut ()>); impl NotSend { fn new() -> Self { Self(std::marker::PhantomData) } } unsafe impl Sync for NotSend {} #[derive(Clone)] struct WatchRecv; impl WatchRecv { fn borrow(&self) -> Ref<'_> { Ref(self, NotSend::new()) } } struct Ref<'a>(&'a WatchRecv, NotSend); fn assert_send<F: std::future::Future + Send>(f: F) -> F { f } impl Ref<'_> { fn len(&self) -> usize { 0 } } async fn another_future() { loop {} } async fn real_main() { let rx = WatchRecv; let mut binding = rx.clone(); assert_send(async move { // /* // This doesn't works. let value = binding.borrow(); let len = value.len(); drop(value); println!("len: {len}"); // */ /*  // This works.  let len = {  let value = binding.borrow();  value.len()  };  */ another_future().await; }).await; println!("{:?}", rx.borrow().len()); }

Metadata

Metadata

Labels

A-async-awaitArea: Async & AwaitA-coroutinesArea: CoroutinesAsyncAwait-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