This odd bit of code emits an error from do_the_thing() saying T does not live long enough:
use std::future::Future;
trait Connection: Send {
type ExecFut<'a>: Future<Output = ()> + Send
where
Self: 'a;
fn transaction<F>(&mut self, _f: F)
where
F: for<'a> FnOnce(&'a mut Self) -> Box<dyn Future<Output = ()> + Send + 'a> + Send,
{
unimplemented!()
}
fn execute<'a>(&'a mut self) -> Self::ExecFut<'a> {
unimplemented!()
}
}
fn do_the_thing<T: Connection>(connection: &mut T) {
connection.transaction(|conn| {
Box::new(async move {
conn.execute().await;
})
});
}
error[E0310]: the parameter type `T` may not live long enough
--> src/main.rs:22:9
|
22 | / Box::new(async move {
23 | | conn.execute().await;
24 | | })
| |__________^ ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
20 | fn do_the_thing<T: Connection + 'static>(connection: &mut T) {
| +++++++++
See it on the playground.
As far as I can tell, there should be no implied bound that 'static is required. Pretty much everything is constrained by 'a and any lifetimes in T should always outlive it regardless. Though obviously there might be something I'm missing.
I've also found a few "solutions" that aren't helpful:
- Adding
'staticas the compiler suggests is not reasonable. It can be worked around but there are realConnections with non-'staticlifetimes that I would like to use. - For some reason removing the
Sendbound on thedyn Futuremakes it pass compilation. This makes zero sense to me. - If I make the function non-generic but use a
Connectionthat is not'staticit compiles as well, seemingly contradicting the compiler error.
The above is not real code; the original motivation stems from using AsyncConnection from the diesel-async crate and similar code structure. However, I hope it is representative of the core issue and that by understanding the problem and potential solutions here, they can be adapted.
From further tinkering and research, I found this issue - GATs: Decide whether to have defaults for
where Self: 'a- in regard to thewhereclause ontype ExecFut<'a>. From the comments in the issue, it seems others have had similar errors from having this bound enforced.There's a workaround provided from the issue, which is to split your trait into two - one for the associated types and another for the methods so that the
where Self: 'aclause can be omitted:Doing that on my example makes it compile but it does start to get messy in my target use-case.
Another workaround that I've seen mentioned was to introduce a dummy function that uses the associated type but not in conjunction with
self. This allows you to omit thewhere Self: 'aclause as well, and thus the code compiles:As a side note, there is another issue linked - Problems with GATs, async, and Send-bounds - that describes an odd interaction between the bounds like above and
Sendwhich may explain why removing aSendbound caused the snippet to compile. Still weird.