Rust 为什么在闭包中克隆数据不能防止出现错误;闭包可能比当前函数“更有效”;?
我用GTK-rs构建了一个GTK应用程序。当我构建主窗口时,我想使用一些动态参数,例如窗口高度。我创建了一个包含所有此类设置的结构,并希望将其用作构建UI的函数的输入参数: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 =
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: >k::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: >k::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这不是相同的错误,而是不同的错误。是的,这是不同的错误。如果没有更多信息,我们将无法帮助您。对不起,我的意思与我在尝试您的方法时从最初的评论中得到的错误相同。