How to solve this “does not live long enough”?

2019-07-29 19:12发布

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.

标签: rust
2条回答
▲ chillily
2楼-- · 2019-07-29 19:22

Read the error messages:

  1. t does not live long enough — it lives until the end of the foo function.
  2. borrowed value must be valid for the lifetime 'ayou 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>
查看更多
女痞
3楼-- · 2019-07-29 19:33

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

查看更多
登录 后发表回答