There is some minimal example library code I would like to use:
struct MyR<'a> {
x: &'a str,
}
struct T {
x: &'static str,
}
impl T {
fn bar<'a>(&'a self) -> MyR {
MyR { x: self.x }
}
}
The following is my code:
trait A<R, F: FnMut(&R)> {
fn foo(&mut self, callback: &mut F);
}
impl<'a, F> A<MyR<'a>, F> for T
where F: FnMut(&MyR<'a>)
{
fn foo(&mut self, callback: &mut F) {
let t = T { x: "l" };
let r = t.bar(); // t does not live long enough (for 'a)
callback(&r);
println!("abc");
}
}
fn test() {
let mut t = T { x: "l" };
let mut i = 1;
t.foo(&mut |x| { i += x.x.len(); });
}
I would like to make a trait that is parametrized by the callback, but I struggled to make it right. If I don't use a trait, it works well:
impl T {
fn foo<F: FnMut(&MyR)>(&mut self, callback: &'a mut F) {
let t = T { x: "l" };
let r = t.bar();
callback(&r);
println!("abc");
}
}
But I cannot do this:
impl T {
fn foo<'a, F: FnMut(&MyR<'a>)>(&mut self, callback: &mut F) {
let t = T { x: "l" };
let r = t.bar();
callback(&r);
println!("abc");
}
}
I know the problem is that t
must outlive 'a
, but I don't know to bound 'a
so that its lifetime is shorter than t
.
I'm using rustc 1.19.0-nightly.
Read the error messages:
t
does not live long enough — it lives until the end of the foo
function.
borrowed value must be valid for the lifetime 'a — you have specified 'a
:
impl<'a, F> A<MyR<'a>, F> for T
where F: FnMut(&MyR<'a>)
This says that for any possible lifetime, the trait will be implemented, so long as F
implements the FnMut
trait.
There's only one possible way to make that work — you have to have a MyR
that is parameterized with the 'static
lifetime. That's the only lifetime that is guaranteed to outlive any arbitrary lifetime.
Let's see where MyR
comes from:
fn bar<'a>(&'a self) -> MyR {
MyR { x: self.x }
}
If you go back and reread The Rust Programming Language section on lifetime elision, you'll recognize that this lifetime specification provides no value. It defines a lifetime and uses it with self
, but it's never tied to any output lifetimes. The code is the same as:
fn bar<'a, 'b>(&'a self) -> MyR<'b>
If you removed the lifetime, then you'd have
fn bar(&self) -> MyR
fn bar<'a>(&'a self) -> MyR<'a> // equivalent
However, neither of these is the 'static
lifetime. Luckily for you, you know that that x
is a &'static str
, so you can just reflect that in your signature and the code will compile:
fn bar(&self) -> MyR<'static>
Spending hours trying different approaches, this seems to work
trait A<F> {
fn foo(&mut self, callback: &mut F);
}
impl<F> A<F> for T
where F: FnMut(&MyR)
{
fn foo(&mut self, callback: &mut F) {
let t = T { x: "l" };
let r = t.bar(); // t does not live long enough (for 'a)
callback(&r);
println!("abc");
}
}
fn main() {
let mut t = T { x: "l" };
let mut i = 1;
t.foo(&mut |x: &MyR| { i += x.x.len(); });
}
The main difference is:
- I have to loose trait a bit so that it takes arbitrary types.
- So that when I impl I don't have to specify lifetime at all.
- I have to type annotate the closure when invoking the function.
Playground