< 返回版块

w 发表于 2021-03-05 14:31

Tags:生命周期,lifetime

昨天在搜索一个问题时发现一个例子:


fn main() {
    let mut p = Processor::new();
    let s = String::from("123");
    {
        p.set_callback(|| println!("{}", s.len()));
    }

    p.invoke();
    println!("last {}", s);
}

struct Processor<'a> {
    callback: Box<dyn Fn() + 'a>,
}

impl<'a> Processor<'a> {
    fn new() -> Processor<'a> {
        Processor { callback: Box::new(|| ()) }
    }
    fn set_callback<CB: 'a + Fn()>(&mut self, c: CB) {
        self.callback = Box::new(c);
    }
    fn invoke(&self) {
        (self.callback)();
    }
}

这段代码是无法通过的,提示如下:



   |
37 |         p.set_callback(|| println!("{}", s.len()));
   |                        --                ^ borrowed value does not live long enough
   |                        |
   |                        value captured here
...
43 | }
   | -
   | |
   | `s` dropped here while still borrowed
   | borrow might be used here, when `p` is dropped and runs the destructor for type `Processor<'_>`
   |
   = note: values in a scope are dropped in the opposite order they are defined

我按照原文的提示将s的定义移到了p之前即可通过。答案地址

休息时候我想了下觉得有点问题,因为在之前看生命周期时没发现变量定义的先后会造成这种问题,于是猜想是不是结构体或是闭包导致的问题,于是重新写了一段代码想验证一下,发现依然是无法通过编译:

    let mut g :Box<dyn Fn()>;
    let s = String::from("123");

    {
        g = Box::new(|| { println!("{}", &s) });
    }

    println!("{}", s);

    // println!("{}", s);

    g();
23 |         g = Box::new(|| { println!("{}", &s) });
   |                      --                   ^ borrowed value does not live long enough
   |                      |
   |                      value captured here
...
31 | }
   | -
   | |
   | `s` dropped here while still borrowed
   | borrow might be used here, when `g` is dropped and runs the destructor for type `Box<dyn Fn()>`
   |
   = note: values in a scope are dropped in the opposite order they are defined

到这里我排除了结构体的问题,觉得是闭包的问题,于是写了下面这段代码发现可以通过编译:

struct Foo<'a> {
            x: Box<Option<&'a String>>,
        }

        let mut x = Foo { x: Box::new(None) };

        let y = String::from("asd");
        {

            x.x = Box::new(Some(&y));
        }

        println!("{:?}", x.x);

到这里我又去翻了下闭包和生命周期相关的章节,发现并没有相关描述,到github搜了下也没有类似issue,麻烦问下这是一种错误的编译器提示还是闭包的引用导致的问题?

评论区

写评论
作者 w 2021-03-08 08:42

感谢老哥。看了下应该是这段话。二者结合导致出现了在区块边缘提示s dropped here while still borrowed,而修改s变量相对位置则可解决问题。

From Niko Matsakis' intro to NLL:

The way that the compiler currently works, assigning a reference into a variable means that its lifetime must be as large as the entire scope of that variable.

And in a later blog post of his:

In particular, today, once a lifetime must extend beyond the boundaries of a single statement [...], it must extend all the way till the end of the enclosing block.

--
👇
johnmave126: 这是一个典型的drop顺序的问题

参考这个回答

--
👇
w: 我再搜一搜,看看issue里有没有

--
👇
uno: 感觉是drop顺序的问题,,你后面的那段代码也是交换一下位置就行了。 ....



johnmave126 2021-03-05 23:48

这是一个典型的drop顺序的问题

参考这个回答

--
👇
w: 我再搜一搜,看看issue里有没有

--
👇
uno: 感觉是drop顺序的问题,,你后面的那段代码也是交换一下位置就行了。 ....


作者 w 2021-03-05 15:53

我再搜一搜,看看issue里有没有

--
👇
uno: 感觉是drop顺序的问题,,你后面的那段代码也是交换一下位置就行了。 ....

作者 w 2021-03-05 15:52

我加了这样的生命周期,得到了和我这个事例一样的错误信息:


struct A<'a>(&'a i32);

impl<'a> A<'a> {
    fn foo(&'a self) {
        println!("{}", self.0);
    }
    fn r(&'a self) -> impl Fn() + 'a {
        || {
            self.foo()
        }
    }
}

fn main() {
    let a = A(&1);
    let b = a.r();
    b();
}

| 21 | ||{self.foo()} | -- ^^^^ borrowed value does not live long enough | | | value captured here 22 | } | - | | | self dropped here while still borrowed | borrow later used here


--
👇
Bai-Jinlin: 我也碰到过和你差不多的问题,我想实现的是类的一个方法返回一个闭包,这里闭包里调用了这个类的方法,并且不move self,但是生命周期不管如何标注都会有问题,不用unsafe根本解决不了。

struct A(i32);
impl A {
    fn foo(&self) {
        println!("{}", self.0);
    }
    fn r(&self) ->impl Fn()+'_{
        ||{self.foo()}
    }

}
fn main() {
    let a = A(1);
    let b = a.r();
    b();
}

这里的r就会出出问题,生命周期标注就算用'a:'b这种也不行,只能改成

    fn r(&self) -> impl Fn() {
        let a = self as *const Self;
        move || unsafe {
            (*a).foo();
        }
    }
uno 2021-03-05 15:48

感觉是drop顺序的问题,,你后面的那段代码也是交换一下位置就行了。

fn main() {
    let s = String::from("123");
    let g:Box<dyn Fn()>;

    {
        g = Box::new(|| { println!("{}", &s) });
    }

    println!("{}", s);

    g();
}

或者直接去掉g的类型定义,也可以。

fn main() {
    let g;
    let s = String::from("123");

    {
        g = Box::new(|| { println!("{}", &s) });
    }

    println!("{}", s);

    g();
}
Bai-Jinlin 2021-03-05 15:10

我也碰到过和你差不多的问题,我想实现的是类的一个方法返回一个闭包,这里闭包里调用了这个类的方法,并且不move self,但是生命周期不管如何标注都会有问题,不用unsafe根本解决不了。

struct A(i32);
impl A {
    fn foo(&self) {
        println!("{}", self.0);
    }
    fn r(&self) ->impl Fn()+'_{
        ||{self.foo()}
    }

}
fn main() {
    let a = A(1);
    let b = a.r();
    b();
}

这里的r就会出出问题,生命周期标注就算用'a:'b这种也不行,只能改成

    fn r(&self) -> impl Fn() {
        let a = self as *const Self;
        move || unsafe {
            (*a).foo();
        }
    }
1 共 6 条评论, 1 页