You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Variable lifetime is wrongly tracked in async fn #98077
|
4 | f = Some(async { value });
| --^^^^^--
| | |
| | borrowed value does not live long enough
| value captured here by generator
5 | core::mem::drop(f);
6 | }
| -
| |
| `value` dropped here while still borrowed
| borrow might be used here, when `f` is dropped and runs the destructor for type `Option<impl Future<Output = i32>>`
|
= note: values in a scope are dropped in the opposite order they are defined
f is manually moved with drop and could not be dropped at the end of the scope after value.
Some bonus weird behavior, this works:
structFoo;asyncfnfoo(){letmut f = None;let value = Foo;
f = Some(async{ value });
core::mem::drop(f);}
But if you add #[derive(Copy, Clone)] to Foo, then it fails with the same error. However, this also does not work:
structFoo;implFoo{fnpush(&mutself){}}asyncfnfoo(){letmut f = None;let value = Foo;
f = Some(async{ value.push()});
core::mem::drop(f);}
We discussed this in our triage meeting today (conversation starts here). There are a few more variations that work:
We can await the value and it works:
asyncfnfoo(){letmut f = None;let value = 0;
f = Some(async{ value }.await);
core::mem::drop(f);}
We can also change the order that f and value are declared:
asyncfnfoo(){let value = 0;letmut f = None;
f = Some(async{ value });
core::mem::drop(f);}
Using async move also works:
asyncfnfoo(){letmut f = None;let value = 0;
f = Some(asyncmove{ value });
core::mem::drop(f);}
I can explain a lot of the behavior, but the fact that drop doesn't help still confuses me.
In the cases that fail, I think f gets inferred to be something like Option<impl Future + 'a>, so f borrows something with lifetime 'a. There can be a couple reasons for the borrow. In the case of value.push(), this is sugar for Foo::push(&'a value). When value is Copy, then the use of value desugars to *&'a value, but when value is not Copy, then the use of it counts as a move and there is no borrowing.
Ignoring the call to drop, when we go out of scope we drop variables in reverse order. This means value gets dropped, and then f potentially has a dangling pointer. That's why we're getting the error. This is also why changing the declaration order fixes the bug.
The part I don't understand is why the compiler doesn't realize f is no longer live after the call to drop(f). This seems like the kind of thing it's normally able to handle. Maybe this has something to do with the fact that we're in an async fn instead of a regular fn?
Activity
eholk commentedon Jun 20, 2022
We discussed this in our triage meeting today (conversation starts here). There are a few more variations that work:
We can await the value and it works:
We can also change the order that
fandvalueare declared:Using
async movealso works:I can explain a lot of the behavior, but the fact that
dropdoesn't help still confuses me.In the cases that fail, I think
fgets inferred to be something likeOption<impl Future + 'a>, sofborrows something with lifetime'a. There can be a couple reasons for the borrow. In the case ofvalue.push(), this is sugar forFoo::push(&'a value). WhenvalueisCopy, then the use ofvaluedesugars to*&'a value, but whenvalueis notCopy, then the use of it counts as a move and there is no borrowing.Ignoring the call to
drop, when we go out of scope we drop variables in reverse order. This meansvaluegets dropped, and thenfpotentially has a dangling pointer. That's why we're getting the error. This is also why changing the declaration order fixes the bug.The part I don't understand is why the compiler doesn't realize
fis no longer live after the call todrop(f). This seems like the kind of thing it's normally able to handle. Maybe this has something to do with the fact that we're in anasync fninstead of a regularfn?@rustbot label +AsyncAwait-Triaged
Enselic commentedon Mar 20, 2026
This issue will be closed by #154155. Please see that PR for more info, such as bisect info.
Rollup merge of rust-lang#154155 - Enselic:async-test, r=mati865
Rollup merge of rust-lang#154155 - Enselic:async-test, r=mati865