2

How to set the value of a struct inside a GTK event in rust? I'm gettting an error when trying to modify the s struct instance below.

use gtk::prelude::*;
use gio::prelude::*;

use gtk::{Application, ApplicationWindow, Button};

struct SomeStruct {
    val: bool,
}

fn main() {
    let mut s = SomeStruct {
    val: false,
    };

 application.connect_activate(|app| {
    let window = ApplicationWindow::new(app);
    window.set_title("test");
    window.set_default_size(350,70);
    
    let button = Button::with_label("Test");

    
    
    button.connect_clicked(|_|{
       s.val = true; // error here
    });

    window.add(&button);
    window.show_all();
    });

    application.run(&[]);
}

Error messages:

error[E0597]: `s` does not live long enough

error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
Mihir Luthra
  • 6,059
  • 3
  • 14
  • 39
Andrzej C
  • 25
  • 5
  • To help you, people should be able to reproduce what you are saying. I would suggest to include all the necessary imports and the exact error message in the question. – Mihir Luthra Nov 28 '20 at 12:08
  • OK, I updated the OP according to your suggestions. – Andrzej C Nov 28 '20 at 12:14
  • I don't know about these GTK functions but the error seems to be because by doing `s.val = true` you are trying to mutate `s` but as it is inside `Fn()` closure, you are not allowed to do that. This may help: [Fn, FnMut, FnOnce](https://stackoverflow.com/questions/30177395/when-does-a-closure-implement-fn-fnmut-and-fnonce). Also `s does not live long enough` seems to be because a `'static` lifetime is expected but `s` lives only till the end of `main()`. – Mihir Luthra Nov 28 '20 at 12:33

1 Answers1

2

Your code has two issues:

  • Although s outlives the GTK application, Rust doesn't know that, whis is why it complains of s not living long enough.
  • ButtonExt::connect_clicked() accepts a Fn, so it's not allowed to mutate data without extra precautions.

The first issue can be fixed by allocating s on the heap and accessing it through a reference-counted pointer, which ensures that the object lives at least as long as the closure that refers to it.

The second issue is easiest to resolve through interior mutability, by wrapping SomeStruct into a RefCell. RefCell holds a value and allows the holder to mutate it through a shared reference to the RefCell. When requesting a mut reference to the inner value, RefCell will check at run-time that no other reference to the value is still live. (This could happen if your closure were to get access to the value and then, while holding the value, called another closure that does the same.)

Those two combined would lead to code like this (untested):

use std::rc::Rc;
use std::cell::RefCell;

fn main() {
    let s = Rc::new(RefCell::new(SomeStruct { val: false }));

    application.connect_activate(|app| {
        let window = ApplicationWindow::new(app);
        window.set_title("test");
        window.set_default_size(350, 70);

        let button = Button::with_label("Test");

        // clone the handle to s and move it into the closure
        let s2 = Rc::clone(&s);
        button.connect_clicked(move |_| {
            s2.borrow_mut().val = true;
        });

        window.add(&button);
        window.show_all();
    });

    application.run(&[]);
}
user4815162342
  • 141,790
  • 18
  • 296
  • 355