在多个“静态闭包"中改变相同的数据 [英] Mutating the same data in multiple 'static closures

查看:40
本文介绍了在多个“静态闭包"中改变相同的数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定一个使用回调将事件传递给库用户的库(例如 GUI 库),我将如何继续在程序中具有适当的可变性?例如:

Given a library (for instance a GUI library) that uses callbacks to communicate events to the library user, how would I proceed to have proper mutability in the program? For instance:

// so I have this `obj` I want to modify when the user clicks buttons
// in the GUI

let same_action = move |_| {
  // modify obj in several ways, e.g. obj.text = "Modified"
  obj.text = "Modified";
}
// --> I had to move `obj` above since `on_click` has a `'static`
// constraint for `F` but that's not really a thing I want to do,
// I want to keep control of `obj` in the outer scope!!!

// --> error because `same_action` does not implement `Fn` since it
// mutates the moved `obj` in it
button1.on_click(same_action);
// --> if the above worked, here we'd have a error because `button1`
// has moved `same_action`
button2.on_click(same_action);
// --> assuming all of the above worked, we'd have a error here about
// unable to use `obj` because it has been moved to same_action
button3.on_click(move |_| obj.text = "Another modifier");

// the library now process the gui and call the callbacks in a loop
// until exit
gui_run();

// --> ..., error cannot use `obj` because it has been moved by
// `same_action`
println!("Final value: {}", obj.text);

有关此问题的关键点,请参阅带有 //--> 的注释.

See the comments with // --> for the critical points of this question.

对于 Rust 中的事件驱动 API,这似乎是一个非常常见的问题.怎么解决的?

This seems like a pretty common problem to worry on event-driven APIs in Rust. How'd one get around it?

推荐答案

如果您需要共享可变数据,您需要一些确保遵循别名规则的容器,最有可能是来自 std::cell.对于Copy 数据,这是Cell,对于其他类型,有RefCell.然后闭包可以使用:

If you need to share mutable data, you need some container that make sure that the aliasing rules are being followed, most likely one from std::cell. For Copy data this is Cell, for other types there's RefCell. Then the closures can use either:

  • Cell/Cell,或
  • TheObject 其中一些字段是 Cells 和 RefCells.
  • Cell<TheObject>/Cell<TheObject>, or
  • TheObject where some fields are Cells and RefCells.

哪个更好取决于您希望可变性的细化程度,以及这是 TheObject 的所有用户的共同需求还是仅针对此特定闭​​包的需求.在第二种情况下,您需要更改 TheObject 的定义,在第一种情况下,您将执行以下操作:

Which is better depends on how granular you want the mutability to be and whether this is a common need for all users of TheObject or just for this particular closure. In the second case, you need to alter the definition of TheObject, in the first you'd do something like this:

let obj = RefCell::new(obj);

let same_action = move |_| {
  obj.borrow_mut().text = "Modified";
}

如果你不能在闭包的捕获值中借用,例如因为 'static 绑定,你可以把 RefCell 放入 Rc.

If you furthermore can't have borrows in the closure's captured values, for example because of a 'static bound, you can put the RefCell into an Rc.

此外,您不能将 same_action 传递给两个按钮,因为无法复制或克隆闭包.一般来说,这是无法做到的,因为它们可能会关闭无法复制或克隆的东西.如果可能,将来可能会允许这样做,现在您可以使用宏、函数(必须将闭包装箱)来解决它,或者如果很简单,只需编写两次闭包即可.

Also, you can't pass same_action to two buttons because closures can't be copied or cloned. In general this can't be done, because they might close over things that can't be copied or cloned. When possible, it may be allowed in the future, for now you can work around it with a macro, a function (that would have to box the closure), or by simply writing the closure twice if it's simple.

这篇关于在多个“静态闭包"中改变相同的数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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