(This post is about upcoming changes in the not-yet-stable 2024 language edition.)
Consider the following code:
use std::future::Future;
pub struct Foo(Bar);
struct Bar;
impl Foo {
pub fn do_thing(&self) -> impl Future<Output = ()> + Send + 'static {
self.with_ref(|bar| bar.do_thing())
}
fn with_ref<R>(&self, f: impl FnOnce(&Bar) -> R) -> R {
f(&self.0)
}
}
impl Bar {
fn do_thing(&self) -> impl Future<Output = ()> + Send + 'static {
async {}
}
}
This code compiles in the 2021 edition. In the 2024 edition (still unstable), it fails, as expected due to the new return-position-impl Trait
capture rules, which specify that Bar::do_thing()
captures the self
lifetime:
error: lifetime may not live long enough
--> src/lib.rs:10:29
|
10 | self.with_ref(|bar| bar.do_thing())
| ---- ^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure `impl Future<Output = ()> + Send + 'static` contains a lifetime `'2`
| has type `&'1 Bar`
The solution to this problem is to use the precise capturing syntax (also unstable, but stabilized for 1.82) to specify that the futures don't capture anything, recovering the previous meaning:
#![feature(precise_capturing)]
pub struct Foo(Bar);
struct Bar;
impl Foo {
pub fn do_thing(&self) -> impl Future<Output = ()> + Send + use<> {
self.with_ref(|bar| bar.do_thing())
}
fn with_ref<R>(&self, f: impl FnOnce(&Bar) -> R) -> R {
f(&self.0)
}
}
impl Bar {
fn do_thing(&self) -> impl Future<Output = ()> + Send + use<> {
async {}
}
}
Notice that the impl Future
s no longer include + 'static
— they don't seem to need it. My question, then, is: is there any remaining use for specifying + 'static
or any other lifetime bound on the type? If so, does it benefit the caller or the callee?
I believe I understand that the reason use<>
has to be introduced is that + use<'a, 'b>
means that the value is valid for the intersection of 'a
and 'b
, whereas + 'a + 'b
means that the value is valid for the union of 'a
and 'b
. But do they differ in any other way? Does + use<>
have all the same effects as + 'static
for the user of the impl Trait
type?
What consequences should we be thinking about for our API designs and semver compatibility when using these new capturing rules and syntax?