为什么在闭包内部克隆数据不能防止错误“闭包可能会超出当前函数的寿命”?

纳乔

我用gtk-rs构建了一个GTK应用程序。在构建主窗口时,我想使用一些动态参数,例如窗口高度。我创建了一个包含所有此类设置的结构,并希望将其用作构建UI的函数的输入参数:

fn main() {
    let application =
        gtk::Application::new(Some("id"), Default::default())
            .expect("Initialization failed...");

    let config = Config {width: 100., height: 100.};
    application.connect_activate(|app| {
        build_ui(app, config.clone());
    });

    // Use config further

    application.run(&args().collect::<Vec<_>>());
}

#[derive(Debug, Clone)]
pub struct Config {
    pub width: f64,
    pub height: f64,
}

fn build_ui(application: &gtk::Application, config: Config) {
    ...
}

我不能config在调用时使用引用,build_ui因为可以在主函数完成后调用此函数,因此config结构不再存在。

我的想法是创建config结构的副本(只有几个原始变量),该副本与原始结构分开存在,因此不会遇到生命周期或所有权问题。

这是正确的方法吗?我究竟做错了什么?我从借用config结构时得到了相同的错误:

error[E0373]: closure may outlive the current function, but it borrows `config`, which is owned by the current function
  --> src/main.rs:36:34
   |
36 |     application.connect_activate(|app| {
   |                                  ^^^^^ may outlive borrowed value `config`
37 |         build_ui(app, config.clone());
   |                       ------ `config` is borrowed here
乙硫

一般说明

最小复制类似问题:

fn move_and_print(s: String) {
    println!("{}", s);
}

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

    let print_cloned_s = || println!("{}", s.clone());

    move_and_print(s);
    print_cloned_s();
}

编译器抱怨:

error[E0505]: cannot move out of `s` because it is borrowed

我想克隆s以避免借用,因此以后可以使用它。那么,编译器怎么能说s是借来的呢?

以前的推理是完全正确的,但是有一个微妙之处:Clone::cloneis的签名clone(&self) -> Self因此,当clone被调用时,数据是由克隆函数借用的

解决方案是创建闭包之前先克隆数据,然后将其移动到闭包中:

fn move_and_print(s: String) {
    println!("{}", s);
}

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

    // I clone `s` BEFORE creating the closure:
    let cloned_s = s.clone();

    // Then I move the cloned data into the closure:
    let print_cloned_s = move || println!("{}", cloned_s);

    move_and_print(s);
    print_cloned_s();
}

解决您的实际错误

就像我说的,您必须克隆配置并将此克隆移到闭包内部:

let cloned_config = config.clone();

application.connect_activate(move |app| {
    build_ui(app, cloned_config.clone());
});

您还必须添加第二个克隆调用,以允许闭包为aFn而不是a FnOnce确实,如果将配置移入build_ui,该函数将无法使用两次。有关更多信息,请参见此问题


如果我很了解您的需求,config则打算将其设为必须共享的只读配置。在这种情况下,我根本不会移动它,例如,将的签名更改build_ui为:

fn build_ui(application: &gtk::Application, config: &Config)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

“错误:闭包可能会超过当前函数的寿命”,但不会超过当前函数的寿命

为什么我的闭包内部的函数的效果不能持久存在?

为什么闭包的可变引用参数没有超过函数调用的寿命?

为什么我不能通过Groovy中的构造函数内部的闭包来初始化最终类变量?

为什么“ for .. in”允许闭包?

为什么不能修改内联方法的闭包参数?

为什么我不能从闭包中返回引用?

为什么不能将变量移出闭包?

为什么 FnMut 闭包会消耗捕获的变量?

为什么不能在类型别名上使用@autoclosure到函数闭包类型?

为什么我不能访问原型函数(使用闭包)?

从函数内的闭包内的闭包中返回数据

有什么办法从闭包内部从函数返回吗?

JavaScript函数,闭包等内部的JavaScript函数

在闭包内部构造具有多个方法的函数,并且错误“语法:闭包声明中不能使用局部变量T”

为什么python嵌套函数不称为闭包?

触发事件以调用闭包内部的函数

为什么使用闭包将函数绑定到对象是错误的?

从函数返回闭包

当闭包(似乎)永远无法访问时,为什么要在闭包的类型注释中使用“内部参数标签”?

为什么闭包变量不被遗忘?

为什么在闭包参数中使用“ &&”?

闭包-为什么这行代码是这样的?

为什么参数通过闭包失效?

(lambda)函数闭包捕获了什么?

为什么我可以在JMeter的暴露道具Hashtable上保存常规的闭包,但不能保存常规的函数呢?

具有显式参数的闭包内部不能使用匿名闭包

JavaScript闭包-帮助我理解为什么内部函数的返回值不返回

Javascript闭包中变量的寿命