Mutating the same data in multiple "static gates"

Given a library (such as a GUI library) that uses callbacks to exchange events with the user of the library, how would I get the correct mutability in the program? For example:

// 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 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 comments from // -->

for critical points of this question.

This seems to be a fairly common issue to worry about over the event driven API in Rust. How can you get around this?

+3


source to share


1 answer


If you need to split volatile data, you need some sort of container that enforces the alias rules, most likely from std::cell

. For data Copy

it is Cell

, for other types there RefCell

. Then closures can use either:

  • Cell<TheObject>

    / Cell<TheObject>

    , or
  • TheObject

    where some fields Cell

    and RefCell

    s.

Which is better depends on how much you want it to be mutable and whether it is a common need for all users TheObject

or just for that particular closure. In the second case you need to change the definition TheObject

, in the first you will do something like this:



let obj = RefCell::new(obj);

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

      

If, in addition, you cannot have borrowings in private captured values, for example due to binding 'static

, you can put RefCell

in Rc

.

Also, you cannot pass same_action

to two buttons as the closure cannot be copied or cloned. In general it is not possible to do this because they can close things that cannot be copied or cloned. Whenever possible, this can be resolved in the future, since you can now work around it with a macro, a function (which should have closed the closure), or simply write the closure twice if that is easy.

+3


source







All Articles