-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The rules for how non-Send local variables "infect" an async function, making its Future type non-Send also, are stricter than they need to be. #63768
Comments
Even if Having rustc try and infer shorter scopes to kill variables before yield points sounds like it might lead to surprising situations when you try and use a variable that appears live which will suddenly make your future See also #57478 about explicitly dropping the variable before the yield point. |
Thanks for clarifying for me. While I was playing with this I was thinking about the interaction between In my mind, borrow checking and " |
In particular, I found it surprising that "use a nested scope" works as a solution. To me, non-lexical lifetimes were sort of about how "we don't need to use nested scopes anymore," and it seems like a shame to regress on that a little. But then again there are a bunch of complexities around NLL that I can't think of an equivalent for here. Like |
@oconnor663 NLL has nothing to do with drop order, so it won't help there. NLL only changed lifetime analysis, which will allow strictly more programs, but won't change the behavior of old programs. |
@KrishnaSannasi for sure, I was only bringing up NLL as a metaphor for what might be possible/desirable to do about the
|
Ok, that looks like it could work, altough I'm not sure if this would make undesirable changes to drop order (haven't really thought about drop order too much before, so I don't know much about the details). Someone else can evaulate that. |
I guess I'd frame it as "looking very closely at the existing drop order and figuring out whether a |
I'm going to remove the 'on deck' label. I think this amounts to needing more precise analysis of what is live, which is a rather complex topic. I do hope we'll address it but leaving this marked as on-deck doesn't seem useful as it's not an issue one would pick up lightly. |
To add, the compiler doesn't respect calling drop before the await point. Using a scope: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6cd230309e9226b37804354aa36db789 |
Bumping this issue. In more complex architectures wherein there are nested scopes, the usual workaround leads to unfriendly code |
Added to #69663 |
I was recently surprised by this not compiling: fn assert_send() {
fn is_send(f: impl Future + Send) {}
is_send(foo()); // ERROR: `Cell<isize>` cannot be shared between threads safely, the trait `Sync` is not implemented for `Cell<isize>`
}
fn spawn_local(x: impl Future) -> impl Future<Output = ()> {
async { todo!() }
}
async fn foo() {
let not_send = async {
let v = RefCell::new(1);
let mut u = v.borrow_mut();
async { }.await;
*u += 1;
};
spawn_local(not_send).await;
} But this working just fine: async fn foo() {
let handle = spawn_local(async {
let v = RefCell::new(1);
let mut u = v.borrow_mut();
async { }.await;
*u += 1;
});
handle.await;
} Even more surprisingly, this doesn't work either: async fn foo() {
spawn_local(async {
let v = RefCell::new(1);
let mut u = v.borrow_mut();
async { }.await;
*u += 1;
}).await;
} |
Any hope this will get fixed soon? |
As far as I know, none of the examples here are erring out. However, I suspect that when the non- So... can we check this off the list this time? Or are we still missing something? 🤔 |
Still missing something, unfortunately. As of 1.79.0 and on Nightly 2024-07-10, this compiles: use tokio::sync::watch::Receiver;
async fn affected_fn(recv: &Receiver<()>) {
{
let b1 = recv.borrow();
let _ = &*b1;
drop(b1);
}
async {}.await;
}
pub fn test_fn(cache: &Receiver<()>) {
fn assert_send(_f: impl std::future::Future + Send) {}
assert_send(affected_fn(cache))
} But this variant of async fn affected_fn(recv: &Receiver<()>) {
let b1 = recv.borrow();
let _ = &*b1;
drop(b1);
async {}.await;
} Nor does this one, which gets rid of the local entirely, and provokes a bizarre "with async fn affected_fn(recv: &Receiver<()>) {
let _ = &*recv.borrow();
async {}.await;
} Changing that line to |
This is very needed in case of match expression and async, especially with non async libraries like Example:
|
Here's an example which I think should compile, but which doesn't (cargo 1.39.0-nightly 3f700ec43 2019-08-19):
The error is "
*mut ()
cannot be sent between threads safely", which is to saymy_future()
is!Send
. I'm surprised by that, because thenonsend
variable is never used after the.await
point, and it's notDrop
. Some other notes:drop(nonsend)
call after thelet nonsend = ...
line doesn't help.my_future
. That is, ifnonsend
is created after the.await
point, the future is stillSend
.Are there any future plans to have the rustc look more closely at which locals do or do not need to be stored across an
.await
?The text was updated successfully, but these errors were encountered: