Closed
Description
I tried this code:
use std::sync::mpsc;
use std::future::Future;
use std::pin::Pin;
async fn awaitpoint(_: ()) { }
async fn failure() {
let (_, rx) = mpsc::channel::<()>();
while let Ok(event) = rx.recv() {
awaitpoint(event).await;
}
}
fn main() {
let _: Pin<Box<dyn Future<Output=()> + Send>> = Box::pin(
async {
failure().await;
}
);
}
Ideally, it would compile. (Obviously it doesn't make much sense, semantically.)
Instead, it produces this error:
error: future cannot be sent between threads safely
--> src/main.rs:16:53
|
16 | let _: Pin<Box<dyn Future<Output=()> + Send>> = Box::pin(
| _____________________________________________________^
17 | | async {
18 | | failure().await;
19 | | }
20 | | );
| |_____^ future created by async block is not `Send`
|
= help: the trait `Sync` is not implemented for `std::sync::mpsc::Receiver<()>`
note: future is not `Send` as this value is used across an await
--> src/main.rs:11:26
|
10 | while let Ok(event) = rx.recv() {
| -- has type `&std::sync::mpsc::Receiver<()>` which is not `Send`
11 | awaitpoint(event).await;
| ^^^^^^ await occurs here, with `rx` maybe used later
12 | }
| - `rx` is later dropped here
help: consider moving this into a `let` binding to create a shorter lived borrow
--> src/main.rs:10:27
|
10 | while let Ok(event) = rx.recv() {
| ^^^^^^^^^
= note: required for the cast to the object type `dyn Future<Output = ()> + Send`
The error message leads me to conclude that the reference &rx
which is being created by autoref is being "held" across the await point. The following workaround fixes it:
while let Ok(event) = { let k= ℞ let y = k.recv(); y } {
(albeit with a clippy FP, rust-lang/rust-clippy#8598)
I tried to produce a repro not involving futures etc., using &mut
references, but I wasn't able to do so.
ISTM that this is sufficiently strange, and the workaround sufficiently unpleasant, that it was worth a report. I'm not sure if I should be tagging this as a diagnostic issue, or what.
Thanks for your attention.
Meta
rustc --version --verbose
:
rustc 1.61.0-nightly (1bfe40d11 2022-03-18)
binary: rustc
commit-hash: 1bfe40d11c3630254504fb73eeccfca28d50df52
commit-date: 2022-03-18
host: x86_64-unknown-linux-gnu
release: 1.61.0-nightly
LLVM version: 14.0.0
Metadata
Metadata
Assignees
Labels
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
Jules-Bertholet commentedon May 13, 2023
@rustbot label A-async-await A-lifetimes
eholk commentedon May 22, 2023
Out of curiosity, if you build this on nightly and pass
-Zdrop-tracking-mir
to rustc, does it compile?This looks like another instance of the issues we've been tracking on #69663.
Basically, what's happening is that the
let Some(x) = rx.recv()
part in the while loop desugars into amatch
expression, andmatch
does not create a temporary scope, meaning any temporary values in the thing you're matching on live for the wholewhile
expression. The workaround to make a block with a let binding in it creates a temporary scope, which is why that works.There's more information at https://doc.rust-lang.org/stable/reference/destructors.html?highlight=scope#temporary-scopes
ijackson commentedon May 22, 2023
Indeed it does. (FTAOD and for my reference, I did
RUSTFLAGS=-Zdrop-tracking-mir nailing-cargo +nightly build
)Thanks.
eholk commentedon May 22, 2023
Great, glad to hear that fixes it! Although, I'm sure a stable solution rather than experimental compiler flag would be better :)
Hopefully we can stabilize
-Zdrop-tracking-mir
and turn it on by default.compiler-errors commentedon Dec 31, 2024
This works now.