E0382 with gtk-rs

Hello everyone,

I coded a small programme that shows a warning dialogue when the swap file is used up above a certain threshold. I use gtk-rs to create the dialogue, which can be closed clicking a button. It took a couple of days to "understand" how to do it, thanks to an earlier thread. This is the code I ended up with:

    let application = Application::new(
        Some("com.github.gtk-rs.examples.basic"),
        Default::default(),
    ).expect("failed to initialize GTK application");

    application.connect_activate(|app| {
        let window = ApplicationWindow::new(app);
        window.set_title("Swap is getting full!");
        window.set_default_size(350, 70);

        let window_clone = window.clone();
        let button = Button::with_label("Click to close me");
        button.connect_clicked(move |_| {
            println!("Clicked!");
            window_clone.close();
        });
        window.add(&button);

        window.show_all();
    });

    application.run(&[]);

Using the window object directly in the connect_clicked() action springs up the E0382 error. My first question is: why so? The description of this error seems unrelated, move is not even referred.

And finally, is there a more elegant way of accessing the window object inside the button action? As it stands this code is ugly and verbose.

Thank you.

Can you please post the entire error message?

Did the closure still have the move keyword in it? If so, that’ll force window to get moved into it, and be inaccessible afterwards.

1 Like

You can use the window object directly in the closure, however you won't be able to access after that. But since you need to, you can cheaply clone it (gtk widgets have interior mutability). Also I don't see anything ugly in the code. It reads top to bottom, the callback is defined in situ, thanks to the interior mutability, you don't have to wrap the widget in a Rc RefCell or Arc Mutex etc.
For a different approach, you can check out Relm, it wraps Gtk-rs but offers an Elm architecture, it also has a nice view! macro which gives ui code a more declarative feel.

1 Like

Here it is:

error[E0382]: borrow of moved value: `window`
  --> src/main.rs:31:9
   |
22 |         let window = ApplicationWindow::new(app);
   |             ------ move occurs because `window` has type `gtk::ApplicationWindow`, which does not implement the `Copy` trait
...
27 |         button.connect_clicked(move |_| {
   |                                -------- value moved into closure here
28 |             println!("Clicked!");
29 |             window.close();
   |             ------ variable moved due to use in closure
30 |         });
31 |         window.add(&button);
   |         ^^^^^^ value borrowed here after move

error: aborting due to previous error

For more information about this error, try `rustc --explain E0382`.
error: could not compile `swap-warn`.

To learn more, run the command again with --verbose.

That makes sense, the problem is downstream of the connect_clicked() action.

As @MoAlyousef said, your problem is that the window object is captured / moved to the callback handler, so it's no longer available outside it. The move keyword for closures, AFAICT, forces the capture of the environment variables that are references as values. But this is not the case, as the window object is not a reference.

With window_clone you get a cheap Rc to the underlying window that can be kept in the handler and moved to it, without making the original window object unavailable to the outer scope.

Take a look at the glib clone macro. You'll still have some boilerplate code for every closure, and it's doing internally the same thing, but I find it nicer to work with because you're getting rid of a line of code and the clone is sent into the closure with the same name it started with.

I'm on my phone right now, but I can type up an example later if you're interested.

I'm on my second substantial project with gtk-rs. It takes a little getting used to, but once you get the hang of the little idiosyncrasies it's not too bad to work with.

1 Like

Have you tried shadowing the window? As in, let window = window.clone()?