Repository navigation
Open
Labels
A-async-awaitArea: Async & AwaitArea: Async & AwaitA-closuresArea: Closures (`|…| { … }`)Area: Closures (`|…| { … }`)A-diagnosticsArea: Messages for errors, warnings, and lintsArea: Messages for errors, warnings, and lintsA-higher-rankedArea: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)Area: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)A-trait-systemArea: Trait systemArea: Trait systemAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.Async-await issues that have been triaged during a working group meeting.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.Diagnostics: Confusing error or lint; hard to understand for new users.D-terseDiagnostics: An error or lint that doesn't give enough information about the problem at hand.Diagnostics: An error or lint that doesn't give enough information about the problem at hand.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
Description
Given the following code: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cace533e183828ffeb2772f434b0611e
use std::future::Future;
use std::pin::Pin;
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[derive(Default)]
struct B(bool);
#[derive(Default)]
struct A {
a: bool,
b: B,
}
impl A {
fn b_mut(&mut self) -> &mut B {
&mut self.b
}
}
async fn entrypoint() {
let mut a = A::default();
let list = vec![];
foo(&mut a, |b| Box::pin(baz(b, &list))).await;
}
async fn foo<'a, F>(a: &'a mut A, f: F)
where
F: for<'b> FnOnce(&'b mut B) -> BoxFuture<'b, ()>,
{
f(a.b_mut()).await;
}
async fn baz(b: &mut B, _: &[u8]) {
b.0 = !b.0;
}The current output is:
error[E0597]: `list` does not live long enough
--> src/lib.rs:25:38
|
25 | foo(&mut a, |b| Box::pin(baz(b, &list))).await;
| --- -----------------^^^^--
| | | |
| | | borrowed value does not live long enough
| | returning this value requires that `list` is borrowed for `'static`
| value captured here
26 | }
| - `list` dropped here while still borrowed
This is correct, but hard to understand.
Ideally the output should look like:
error[E0597]: `list` does not live long enough
--> src/lib.rs:25:38
|
25 | foo(&mut a, |b| Box::pin(baz(b, &list))).await;
| --- -----------------^^^^--
| | | |
| | | borrowed value does not live long enough
| | returning this value requires that `list` is borrowed for `'static`
| value captured here
26 | }
| - `list` dropped here while still borrowed
|
| = note: the lifetime cannot be shortened from `'static` to `'b` because closures are contravariant: https://doc.rust-lang.org/nomicon/subtyping.html#variance
|
| = note: the closure passed to `foo` must be valid for any lifetime, including the `'static` lifetime
28 | async fn foo<'a, F>(a: &'a mut A, f: F)
| ^^^^
| F: for<'b> FnOnce(&'b mut B) -> BoxFuture<'b, ()>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| due to the bound here
Reacted by Esteban Kuber and Iizuki
Metadata
Metadata
Assignees
Labels
A-async-awaitArea: Async & AwaitArea: Async & AwaitA-closuresArea: Closures (`|…| { … }`)Area: Closures (`|…| { … }`)A-diagnosticsArea: Messages for errors, warnings, and lintsArea: Messages for errors, warnings, and lintsA-higher-rankedArea: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)Area: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)A-trait-systemArea: Trait systemArea: Trait systemAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.Async-await issues that have been triaged during a working group meeting.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.Diagnostics: Confusing error or lint; hard to understand for new users.D-terseDiagnostics: An error or lint that doesn't give enough information about the problem at hand.Diagnostics: An error or lint that doesn't give enough information about the problem at hand.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
Activity
jyn514 commentedon Sep 23, 2022
In case it helps, here's one possible way to make the code compile: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=66d32bc16434e74e2befb950629307b5
It won't work in general since the async block could borrow the slice.
vincenzopalazzo commentedon Sep 26, 2022
@rustbot claim
as discussed with @eholk and @tmandry let's reproduce it and see how it goes with the result
eholk commentedon Sep 26, 2022
@rustbot AsyncAwait-Triaged
tmandry commentedon Sep 26, 2022
The problem with the code is that the closure wants to return some data borrowed from its environment when it's being asked to return data that is valid for
'b, the lifetime of its argument.The compiler tries to relate these two lifetimes, which aren't related at all (
'bis not related to any other lifetime), and then falls back to thinking the return type must be'static. This results in it saying thatlistmust be borrowed for'static(Confusing)listis dropped while still borrowed (Also confusing, because this message usually refers to the thing that borrowedlist: the capturing closure in this case. But the idea thatlistmust be borrowed past the end of the function has everything to do with the fact that the closure's return type is inferred to be'staticand nothing to do with the fact that it's captured. EDIT: if you write a version that captures&listbut doesn't return it, the code compiles.)I don't think contravariance is involved.
danielhenrymantilla commentedon Oct 1, 2022
Yeah, this compiles:
It's basically https://users.rust-lang.org/t/argument-requires-that-is-borrowed-for-static/66503/2?u=yandros
What Rust very sorely needs is
for<'b where 'env : 'b>kind of bounds, and once we get them, the diagnostics could suggest it when running into this kind of scenario