gpt4 book ai didi

rust - “temporary value dropped while borrowed”与捕获闭包

转载 作者:行者123 更新时间:2023-12-03 11:41:00 27 4
gpt4 key购买 nike

请考虑以下示例(playground):

struct Animal<'a> {
format: &'a dyn Fn() -> (),
}

impl <'a>Animal<'a> {
pub fn set_formatter(&mut self, _fmt: &'a dyn Fn() -> ()) -> () {} // Getting rid of 'a here satisfies the compiler
pub fn bark(&self) {}
}

fn main() {
let mut dog: Animal = Animal { format: &|| {()} };
let x = 0;
dog.set_formatter(&|| {
println!("{}", x); // Commenting this out gets rid of the error. Why?
});
dog.bark(); // Commenting this out gets rid of the error. Why?
}
这给出了以下编译错误:
Compiling playground v0.0.1 (/playground)
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:13:24
|
13 | dog.set_formatter(&|| {
| ________________________^
14 | | println!("{}", x); // Commenting this out gets rid of the error. Why?
15 | | });
| | ^ - temporary value is freed at the end of this statement
| |_____|
| creates a temporary which is freed while still in use
16 | dog.bark(); // Commenting this out gets rid of the error. Why?
| --- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value

error: aborting due to previous error

For more information about this error, try `rustc --explain E0716`.
error: could not compile `playground`

To learn more, run the command again with --verbose.
之所以这样是有意义的,是因为我传递给 dog.set_formatter(...)的闭包确实是一个临时类(我想)是在执行继续进行 dog.bark();时释放的。
我知道在 set_formatter的实现中摆脱显式生命周期注释似乎可以满足编译器的要求(请注意,在 'a之前缺少 dyn):
pub fn set_formatter(&mut self, _fmt: & dyn Fn() -> ()) -> () {}
但是,我不了解以下内容:
  • 当我在闭包内注释掉println!("{}", x);时,为什么问题消失了?我仍在传递一个临时文件,我希望编译器会提示,但事实并非如此。
  • 当我在最后注释掉dog.bark();时,为什么问题仍然存在?同样,我仍在传递一个临时闭包,该闭包已释放,但现在编译器很高兴。为什么?
  • 最佳答案

    首先要了解的是,&|| ()的生命周期为'static:

    fn main() {
    let closure: &'static dyn Fn() = &|| (); // compiles
    }
    值得一提的另一件事是,闭包的生存期不能超过从其环境捕获的任何变量的生存期,这就是为什么如果我们尝试将非静态变量传递给我们的静态闭包,则编译失败:
    fn main() {
    let x = 0; // non-static temporary variable
    let closure: &'static dyn Fn() = &|| {
    println!("{}", x); // x reference captured by closure
    }; // error: trying to capture non-static variable in static closure
    }
    我们将回到这一点。无论如何,因此,如果我有一个对引用通用的结构,并且将其传递给 'static引用,那么我将拥有该结构的 'static实例:
    struct Dog<'a> {
    format: &'a dyn Fn(),
    }

    fn main() {
    let dog: Dog<'static> = Dog { format: &|| () }; // compiles
    }
    要了解的第二件事是,一旦实例化类型,就无法更改它。这包括其任何通用参数,包括生命周期。一旦有了 Dog<'static>,它将始终是 Dog<'static>,对于某些比 Dog<'1>短的匿名生命周期 '1,您将无法将其转换为 'static
    这有一些很深的含义,其中之一就是您的 set_formatter方法可能不会按照您认为的方式工作。拥有 Dog<'static>后,您只能将 'static格式化程序传递给 set_formatter。该方法如下所示:
    impl<'a> Dog<'a> {
    fn set_formatter(&mut self, _fmt: &'a dyn Fn()) {}
    }
    但是由于我们知道我们正在使用 Dog<'static>,因此可以用 'a替换通用生命周期参数 'static来查看我们真正在使用什么:
    // what the impl would be for Dog<'static>
    impl Dog {
    fn set_formatter(&mut self, _fmt: &'static dyn Fn()) {}
    }
    因此,现在我们已经摆脱了所有这些背景,让我们来解决您的实际问题。

    Why does the problem go away when I comment out println!("{}", x); inside the closure? I'm still passing a temporary which I expect the compiler to complain about, but it doesn't.


    为什么失败,并带有注释:
    struct Dog<'a> {
    format: &'a dyn Fn(),
    }

    impl<'a> Dog<'a> {
    fn set_formatter(&mut self, _fmt: &'a dyn Fn()) {}
    }

    fn main() {
    let mut dog: Dog<'static> = Dog { format: &|| () };

    // x is a temporary value on the stack with a non-'static lifetime
    let x = 0;

    // this closure captures x by reference which ALSO gives it a non-'static lifetime
    // and you CANNOT pass a non-'static closure to a Dog<'static>
    dog.set_formatter(&|| {
    println!("{}", x);
    });
    }
    通过注释掉行 println!("{}", x);来“修复”此错误的原因是,由于它不再借用非 'static变量 'static,因此使闭包再次具有 x生存期。

    Why does the problem go away when I comment out dog.bark(); at the end? Again, I'm still passing a temporary closure which is freed but now the compiler is happy. Why?


    这种奇怪的边缘情况似乎只有在我们没有用 dog显式地标记 Dog<'static>变量时才会发生。当变量没有显式类型注释时,编译器会尝试推断其类型,但这样做会很懒惰,并尝试尽可能地灵活,从而使程序员有疑问的利益,从而可以编写代码编译。即使没有 dog.bark(),它实际上也应该引发编译错误,但这并不是出于任何神秘原因。关键是不是导致代码无法编译的 dog.bark()行,无论如何,它都不应该在 set_formatter行进行编译,但是由于任何原因,编译器都不会费心抛出错误,直到您再次尝试使用 dog为止在违规行之后。即使只是删除 dog也会触发错误:
    struct Dog<'a> {
    format: &'a dyn Fn(),
    }

    impl<'a> Dog<'a> {
    fn set_formatter(&mut self, _fmt: &'a dyn Fn()) {}
    }

    fn main() {
    let mut dog = Dog { format: &|| () };
    let x = 0;

    dog.set_formatter(&|| {
    println!("{}", x);
    });

    drop(dog); // triggers "temp freed" error above
    }
    既然我们已经走了这么远,让我们回答您的非官方的第三个问题,由我解释:

    Why does getting rid of the 'a in the set_formatter method satisfy the compiler?


    因为它更改了 Dog<'static>的有效内容:
    // what the impl would be for Dog<'static>
    impl Dog {
    fn set_formatter(&mut self, _fmt: &'static dyn Fn()) {}
    }
    变成这个:
    // what the impl would now be for Dog<'static>
    impl Dog {
    fn set_formatter(&mut self, _fmt: &dyn Fn()) {}
    }
    因此,现在您可以将非 'static闭包传递给 Dog<'static>,尽管这是没有意义的,因为该方法实际上没有执行任何操作,并且当您实际尝试在 Dog<'static>结构中设置闭包时,编译器会再次发出提示。

    关于rust - “temporary value dropped while borrowed”与捕获闭包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65985081/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com