Rust 为什么在闭包中克隆数据不能防止出现错误;闭包可能比当前函数“更有效”;?

Rust 为什么在闭包中克隆数据不能防止出现错误;闭包可能比当前函数“更有效”;?,rust,gtk,lifetime,borrowing,gtk-rs,Rust,Gtk,Lifetime,Borrowing,Gtk Rs,我用GTK-rs构建了一个GTK应用程序。当我构建主窗口时,我想使用一些动态参数,例如窗口高度。我创建了一个包含所有此类设置的结构,并希望将其用作构建UI的函数的输入参数: fn main() { let application = gtk::Application::new(Some("id"), Default::default()) .expect("Initialization failed..."); let config =

我用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) {
    ...
}
一般说明 类似问题的最小复制:

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();
}
编译器抱怨:

错误[E0505]:无法移出's',因为它是借用的
我想克隆
s
,以避免借用,从而允许以后使用它。那么,编译器如何说
s
是借用的呢

前一种推理是完全正确的,但是,有一个微妙之处:签名是
clone(&self)->self
。因此,当调用
clone
时,数据由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());
});
解决您的实际错误 正如我所说,您必须克隆配置并将此克隆移动到闭包中:

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());
});
您还必须添加第二个克隆调用,以允许闭包是
Fn
而不是
FnOnce
。事实上,如果将配置移动到
构建ui
中,则该函数不能使用两次。有关更多信息,请参阅


如果我很了解您的需要,
config
是一种必须共享的只读配置。在这种情况下,我根本不会移动它,例如通过将
build\u ui
的签名更改为:

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

我不想说认可的答案不太准确。这是正确的,但与操作代码有细微的区别。实际上,如果仔细阅读原始代码,就没有理由相信
rustc
不能得出局部变量
config
connect\u activate
函数调用更有效的结论。它出于其他原因拒绝了它

更准确的最小可重复性示例为:

fn参考和打印(s:&str){
println!(“{}”,s);
}
fn闭包和打印(f:f){
f();
}
fn main(){
让我们“你好”;
参考和打印;
闭包和打印(||){
println!(“{}”,s);
});
参考和打印;
}
这是一份汇编。但是,如果只更改一行:


fn closure_和_printI看到了您的评论并尝试了一下,结果无效。我不确定我是否理解您的意思,但尝试您的示例会返回相同的错误:
无法移出'Fn'闭包中捕获的变量
@Jonas这不是相同的错误,而是不同的错误。是的,这是不同的错误。如果没有更多信息,我们将无法帮助您。对不起,我的意思与我在尝试您的方法时从最初的评论中得到的错误相同。