我正在学习 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 || {
| ^^^^^^^^^^^^^^^^^^^
最佳答案
But as far as I know, with or without clone, both have the same lifetime.
这并不完全正确。如果您克隆一个变量,您现在有该变量的两个版本。每个都可以由不同的所有者拥有,因此两者可以有不同的生命周期。这正是你的情况,只是有点隐藏——多亏了闭包魔法。
让我们再看看你的代码(我更改了几个变量名以便稍后明确引用它们):
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
它不会永远存在。
关于rust - 为什么需要这个克隆?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43146645/