在多个“静态闭包”中改变相同的数据

Mutating the same data in multiple 'static closures

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

// 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);

请参阅带有// -->的评论以了解此问题的关键点。

这似乎是 Rust 中事件驱动 API 的一个非常普遍的问题。 如何绕过它?

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

  • Cell<TheObject>/Cell<TheObject>,或
  • TheObject 其中一些字段是 Cells 和 RefCells.

哪个更好取决于您希望可变性的粒度如何,以及这是 TheObject 所有用户的共同需求还是仅针对此特定闭​​包。在第二种情况下,你需要改变 TheObject 的定义,在第一种情况下你会做这样的事情:

let obj = RefCell::new(obj);

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

如果您还不能在闭包的捕获值中借用,例如由于 'static 绑定,您可以将 RefCell 放入 Rc.

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