为什么需要这个克隆?

时间:2017-03-31 17:38:52

标签: rust lifetime

我正在学习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 &gtk::TextBuffer, found &gtk::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:&gtk::TextBuffer]` will meet its required lifetime bounds
  --> src/main.rs:19:9
   |
19 |         timeout_add_seconds(1, move || {
   |         ^^^^^^^^^^^^^^^^^^^

1 个答案:

答案 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_bclone()签名为clone(&T) -> T
  • buffer_c&TextBuffer再次(借鉴b

let ref buffer = b;行进一步迷惑了这一点。写let buffer = &b;更加惯用(两个版本都做同样的事情)。

如果我们不克隆,为什么Rust会抱怨?内部闭包是(timeout_add_seconds() 'static(更正式地说:“满足'static要求)。这意味着闭包不能引用任何不会永远存在的东西('static)。如果我们不克隆,内部封闭将引用不会永远存在的buffer_a