如何将数据移动到多个Rust闭包中? [英] How to move data into multiple Rust closures?

查看:119
本文介绍了如何将数据移动到多个Rust闭包中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在一个简单的GTK应用程序中有两个小部件:

I have a two widgets in a simple GTK app:

extern crate gdk;
extern crate gtk;

use super::desktop_entry::DesktopEntry;

use gdk::enums::key;
use gtk::prelude::*;

pub fn launch_ui(_desktop_entries: Vec<DesktopEntry>) {
    gtk::init().unwrap();

    let builder = gtk::Builder::new_from_string(include_str!("interface.glade"));

    let window: gtk::Window = builder.get_object("main_window").unwrap();
    let search_entry: gtk::SearchEntry = builder.get_object("search_entry").unwrap();
    let list_box: gtk::ListBox = builder.get_object("list_box").unwrap();

    window.show_all();

    search_entry.connect_search_changed(move |_se| {
        let _a = list_box.get_selected_rows();
    });

    window.connect_key_press_event(move |_, key| {
        match key.get_keyval() {
            key::Down => {
                list_box.unselect_all();
            }
            _ => {}
        }
        gtk::Inhibit(false)
    });

    gtk::main();
}

我需要从两个事件中更改list_box.我有两个move闭包,但是由于出现错误,无法同时将list_box移到两个闭包中:

I need to change list_box from both events. I have two closures that move, but it is not possible to move list_box to both closures simultaneously as I get the error:

error[E0382]: capture of moved value: `list_box`

我该怎么办?

推荐答案

如Shepmaster的回答所述,您只能将一个值移出变量一次,并且编译器将阻止您第二次这样做.我将尝试为此用例添加一些特定的上下文.大部分是因为我曾经使用过C时代的GTK,而我只是在gtk-rs文档中进行了查找,所以我确定我得到了一些细节错误,但是我认为一般要点是正确的

As explained in Shepmaster's answer, you can only move a value out of a variable once, and the compiler will prevent you from doing it a second time. I'll try to add a bit of specific context for this use case. Most of this is from my memory of having used GTK from C ages ago, and a few bits I just looked up in the gtk-rs documentation, so I'm sure I got some details wrong, but I think the general gist is accurate.

首先让我们看看为什么首先需要将值移到闭包中.您在两个闭包中调用list_box的方法都引用了self,因此您实际上并没有使用闭包中的列表框.这意味着在没有move说明符的情况下定义两个闭包将是完全有效的–您只需要对list_box的只读引用,就可以一次拥有多个只读引用,而list_box寿命至少与封盖一样长.

Let's first take a look at why you need to move the value into the closures in the first place. The methods you call on list_box inside both closures take self by reference, so you don't actually consume the list box in the closures. This means it would be perfectly valid to define the two closures without the move specifiers – you only need read-only references to list_box, you are allowed to have more than one read-only reference at once, and list_box lives at least as long as the closures.

但是,虽然允许您在不将list_box移入它们的情况下定义两个闭包,但是您不能将以这种方式定义的闭包传递给gtk-rs:仅所有连接事件处理程序的函数接受静态"功能,例如

However, while you are allowed to define the two closures without moving list_box into them, you can't pass the closures defined this way to gtk-rs: all functions connecting event handlers only accept "static" functions, e.g.

fn connect_search_changed<F: Fn(&Self) + 'static>(
    &self, 
    f: F
) -> SignalHandlerId

处理程序的类型F具有特征绑定Fn(&Self) + 'static,这意味着闭包根本不能容纳任何引用,或者它持有的所有引用必须具有静态生存期.如果我们不将list_box移到闭包中,则闭包将保留对其的非静态引用.因此,在将函数用作事件处理程序之前,我们需要先删除参考.

The type F of the handler has the trait bound Fn(&Self) + 'static, which means that the closure either can't hold any references at all, or all references it holds must have static lifetime. If we don't move list_box into the closure, the closure will hold a non-static reference to it. So we need to get rid of the reference before being able to use the function as an event handler.

为什么gtk-rs施加此限制?原因是gtk-rs是一组C库的包装,并且指向回调的指针最终传递到基础glib库.由于C没有任何生存期的概念,因此安全地执行此操作的唯一方法是要求没有任何引用可能变得无效.

Why does gtk-rs impose this limitation? The reason is that gtk-rs is a wrapper around a set of C libraries, and a pointer to the callback is eventually passed on to the underlying glib library. Since C does not have any concept of lifetimes, the only way to do this safely is to require that there aren't any references that may become invalid.

我们现在已经确定,我们的闭包不能包含任何引用.我们仍然需要从闭包中访问list_box,那么我们有什么选择呢?如果只有一个闭包,则使用move可以解决问题–通过将list_box移到闭包中,闭包将成为其所有者.但是,我们已经看到这对多个闭包无效,因为我们只能将list_box移动一次.我们需要找到一种拥有多个多个所有者的方法,Rust标准库提供了这样一种方法:引用计数指针RcArc.前者用于只能从当前线程访问的值,而后者可以安全地移至其他线程.

We have now established that our closures can't hold any references. We still need to access list_box from the closures, so what are our options? If you only have a single closure, using move does the trick – by moving list_box into the closure, the closure becomes its owner. However, we have seen that this doesn't work for more than one closure, because we can only move list_box once. We need to find a way to have multiple owners for it, and the Rust standard library provides such a way: the reference-counting pointers Rc and Arc. The former is used for values that are only accessed from the current thread, while the latter is safe to move to other threads.

如果我没记错的话,glib在主线程中执行所有事件处理程序,并且闭包的特征范围反映了这一点:闭包不需要是SendSync,因此我们应该能够用Rc解决. Morevoer,我们只需要对闭包中的list_box进行读取访问,因此在这种情况下,我们不需要RefCellMutex来实现内部可变性.总而言之,您所需要做的只是这个:

If I remember correctly, glib executes all event handlers in the main thread, and the trait bounds for the closure reflect this: the closure isn't required to be Send or Sync, so we should be able to make do with Rc. Morevoer, we only need read access to list_box in the closures, so we don't need RefCell or Mutex for interior mutability in this case. In summary, all you need is probably this:

use std::rc::Rc;
let list_box: gtk::ListBox = builder.get_object("list_box").unwrap();
let list_box_1 = Rc::new(list_box);
let list_box_2 = list_box_1.clone();

现在,您有两个指向同一列表框的拥有"指针,这些指针可以移到两个闭包中.

Now you have two "owned" pointers to the same list box, and these pointers can be moved into the two closures.

免责声明:由于您的示例代码不是独立的,因此我无法真正测试其中的任何一个.

Disclaimer: I couldn't really test any of this, since your example code isn't self-contained.

这篇关于如何将数据移动到多个Rust闭包中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆