rust - 为什么需要这个克隆?

标签 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 || {
   |         ^^^^^^^^^^^^^^^^^^^

最佳答案

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/

相关文章:

windows - 传递 CString 后跟 int 时 FFI 中的错误

rust - 尝试为结构设置带有生存期的返回值时,由于需求冲突,因此无法为autoref推断适当的生存期

sfml - Rust (+SFML) - 如何在受生命周期参数限制时避免额外的对象构造?

rust - 将向量传递给函数的替代方法?

rust - 如何返回对 optional 结构字段内值的引用?

rust - 如何检查两个变量是否都是 Some?

sql - 如何使用Diesel执行 `UPDATE FROM`?

rust - 如何将函数参数中的项推送到函数参数中的向量 - 不同的生命周期问题

compiler-errors - 链接函数调用与使用中间变量

rust - 通过共享框 ptr 访问时如何使我的结构字段可变?