我正在学习Rust并使用Rust和Gtk创建一个非常简单的应用程序:
extern crate gtk;
use gtk::prelude::*;
use gtk::{Window, WindowType, TextView, TextBuffer, timeout_add_seconds};
fn make_window() {
let window = Window::new(WindowType::Toplevel);
let textview = TextView::new();
window.add(&textview);
let buffer = match textview.get_buffer() {
Some(x) => x,
None => panic!("Textview did not contain a buffer."),
};
buffer.connect_changed(move |buffer: &TextBuffer| {
let b = buffer.clone(); // Why is this clone needed?
timeout_add_seconds(1, move || {
let ref buffer = b;
Continue(false)
});
});
window.show_all();
}
fn main() {
if gtk::init().is_err() {
println!("Failed to initialize GTK.");
return;
}
make_window();
gtk::main();
}
我很困惑为什么在内部关闭之前需要buffer.clone()
。
如果我把它遗漏,我会收到有关生命的错误。但据我所知,有或没有克隆,两者都有相同的寿命。那为什么一个工作而另一个工作呢?
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:18:17
|
18 | let b = buffer; // Why is this clone needed?
| ^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 17:54...
--> src/main.rs:17:55
|
17 | buffer.connect_changed(move |buffer: &TextBuffer| {
| _______________________________________________________^ starting here...
18 | | let b = buffer; // Why is this clone needed?
19 | | timeout_add_seconds(1, move || {
20 | | let ref buffer = b;
21 | | Continue(false)
22 | | });
23 | | });
| |_____^ ...ending here
note: ...so that expression is assignable (expected >k::TextBuffer, found >k::TextBuffer)
--> src/main.rs:18:17
|
18 | let b = buffer; // Why is this clone needed?
| ^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure@src/main.rs:19:32: 22:10 b:>k::TextBuffer]` will meet its required lifetime bounds
--> src/main.rs:19:9
|
19 | timeout_add_seconds(1, move || {
| ^^^^^^^^^^^^^^^^^^^
答案 0 :(得分:3)
但据我所知,无论有无克隆,两者都有相同的生命周期。
这不是真的。如果克隆变量,则现在有两个版本的变量。每个都可以由不同的所有者拥有,因此两者可以具有不同的寿命。这正是你的情况下发生的事情,它只是有点隐藏 - 感谢关闭魔法。
让我们再看一下你的代码(我改变了一些变量名,以便稍后明确地引用它们):
buffer_a.connect_changed(move |buffer_b: &TextBuffer| {
let b = buffer_b.clone(); // Why is this clone needed?
timeout_add_seconds(1, move || {
let ref buffer_c = b;
Continue(false)
});
});
这里,变量b
是通过克隆创建的,并且首先存在于外部闭包中(它是一个局部变量)。但是它在内部闭包中使用,它是一个move
闭包。因此,b
会将移动到内部闭包中,然后拥有 TextBuffer
。是:内部闭包拥有缓冲区b
。这意味着b
的生命与封闭的完全一样;从原来的生命中独立!
为确保我们了解所有内容,只需检查各种变量的类型:
buffer_a
:输入TextBuffer
buffer_b
:输入&TextBuffer
(可能借用buffer_a
)b
:再次输入TextBuffer
(我们克隆buffer_b
,clone()
签名为clone(&T) -> T
)buffer_c
:&TextBuffer
再次(借鉴b
) let ref buffer = b;
行进一步迷惑了这一点。写let buffer = &b;
更加惯用(两个版本都做同样的事情)。
如果我们不克隆,为什么Rust会抱怨?内部闭包是(timeout_add_seconds()
) 'static
(更正式地说:“满足'static
要求)。这意味着闭包不能引用任何不会永远存在的东西('static
)。如果我们不克隆,内部封闭将引用不会永远存在的buffer_a
。