Moved values and captured in Fn closure

I'm trying to write a GTK application in Rust with gtk-rs. I always get this kind of errors:

    error[E0382]: use of moved value: `self`
   --> src/main.rs:199:30
    |
43  |     fn build_ui(&'static mut self, application: &gtk::Application)
    |                 ----------------- move occurs because `self` has type `&mut data`, which does not implement the `Copy` trait
...
177 |         prac.connect_clicked(move |_| {
    |                              -------- value moved into closure here
...
180 |             let b = img_array[self.volt as usize];
    |                               ---- variable moved due to use in closure
...
199 |         next.connect_clicked(move |_| {
    |                              ^^^^^^^^ value used here after move
200 |             if self.volt == 99 {
    |                ---- use occurs due to use in closure

And this one:

error[E0594]: cannot assign to `self.van`, as `Fn` closures cannot mutate their captured variables
   --> src/main.rs:218:17
    |
218 |                 self.van = self.van - 1;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^ cannot assign
    |
help: consider changing this to accept closures that implement `FnMut`
   --> src/main.rs:199:30
    |
199 |           next.connect_clicked(move |_| {
    |  ______________________________^
200 | |             if self.volt == 99 {
201 | |                 let dialog = MessageDialog::new(Some(&window5),
202 | |                     DialogFlags::MODAL,
...   |
253 | |             }
254 | |         });
    | |_________^

I think that these errors are connected, because both of them are because moving to the closure during signal handling. My goal is to increase a variable (volt) when next is clicked. Moreover I'd also like to use it later in other functions (as you can see here). Can anyone help please?

EDIT:
Here's the whole code (it's a huge mess, but I've only started learning rust, and coming from Python is really hard...): https://pastebin.com/pC8yZ1hK
Here's the glade file also: https://pastebin.com/UsmjdG7F

Basically, a mutable reference must be unique. You are trying to put a mutable reference into two different closures at the same time, violating the uniqueness requirement. The second error is that the closures are not called mutably, so you must use some kind of interior mutability (typically some kind of Cell) to modify the data.

I'm also a bit worried about that &'static mut self. How did you obtain a static mutable reference? With Box::leak?

I recommend the following approach:

  1. Put self in an Rc instead of using static mutable references.
  2. For each closure that needs access, make a clone of the Rc before creating the closure, then move the clone into the closure.
  3. Use Cell or RefCell to wrap values that you need to modify. Prefer Cell when possible (e.g. integers and other Copy types).

Note that you should probably downgrade the rcs you move into the event handlers into a Weak to avoid leaking memory, although you don't seem to be worried about that as you already have &'static mut which cannot be obtained soundly without leaking memory.

3 Likes

First of all, thank you for the answer. I've just started learning rust 4 days ago, so I'm on a really basic level. So thanks for the references also.

I'm using a structure called data, and I've build_ui inside an impl data {} (that's self), and I'm calling build_ui from fn main. I'll share more parts of my code to provide a better view of what I'm doing.

Now I'm on a trip, but I'll edit my post as soon as I get home.

EDIT: I've updated my post with the links to the code.

& and &mut are temporary borrows. They are anchored to a certain scope, and can't be used outside of it.

They are not like passing objects by reference or pointer in other languages. They are more like read-shared (&) or exclusive (&mut) locks that prevent the original object from being moved or destroyed while the borrows exist.

It's very important to remember that &mut isn't merely mutability. It's exclusive access in the most restrictive sense.

That 'static won't work. When you try to do something that is impossible with usual temporary references the compiler gives up and suggests a special case of 'static which is totally useless 99% of the time (it's either for literals compiled into the program or leaked memory). If you see compiler demanding 'static read it as "references are not allowed at all here!".

You probably need Arc to share objects and Arc<Mutex> to share them and be able to mutate them.

2 Likes