explicit lifetime required in the type of `test` when creating a closure using gtk-rs

Here is an example project:

use gtk::prelude::*;
use gtk::{Application, ApplicationWindow};

fn main() {
    let app = Application::builder()
        .application_id("org.example.HelloWorld")
        .build();
    app.connect_activate(|app| {
        let win = ApplicationWindow::builder().application(app).build();

        example(&win);

        win.connect_add(move |_, _| {
            example(&win);
        });

        win.show_all();
    });

    app.run();
}

fn example(test: &ApplicationWindow) {
    test.connect_show_menubar_notify(move |_| {
        example(test);
    });
}

The error from cargo run :

error[E0621]: explicit lifetime required in the type of `test`
  --> src/main.rs:22:10
   |
21 | fn example(test: &ApplicationWindow) {
   |                  ------------------ help: add explicit lifetime `'static` to the type of `test`: `&'static ApplicationWindow`
22 |     test.connect_show_menubar_notify(move |_| {
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'static` required

The dependency I use is gtk = "0.14.0" but I also experienced this issue with the gtk-rs git version.

Where is the issue? I'm a beginner; what is the change I have to do?

Please also mention if a question was already posted on a different platform to avoid the need for the people who are willing to help you to independently come up with the same answer twice.

https://stackoverflow.com/questions/68229263/explicit-lifetime-required-in-the-type-of-test-when-creating-a-closure-using-g

I’m not really familiar with gtk-rs but the method connect_show_menubar_notify seems to accept a callback that is intended to be called at a later time than when the call to connect_show_menubar_notify itself happened. Thus its type

fn connect_show_menubar_notify<F: Fn(&Self) + 'static>(
    &self,
    f: F
) -> SignalHandlerId

features a 'static bound which basically outlaws the passed closure from capturing short-lived things like references. The reference type in Rust, i.e. &T is intended for short-lived referencing and offers low/zero overhead (because it’s implemented to just use pointers), but there are restrictions coming from Rust’s memory model around ownership and borrowing. Long story short: The closure argument of connect_show_menubar_notify is not allowed to capture values that aren’t “owned”, in particular not a reference like in your example test: &ApplicationWindow.

Capturing refers to local variables that are mentioned in the body of a clusure, like test appearing in

    move |_| {
        example(test);
    }

The move keyword indicates that the closure takes ownership of the value in the variable test, however this way the closure still only owns a reference. In this particular example the type ApplicationWindow itself is a smart pointer, so you can rather cheaply create a new, owned ApplicationWindow from a reference to an existing one. This will create a setting of shared ownership not unlike usage of an Rc<T> in Rust. More on this way of representing gtk objects can be found here:

gtk-rs.org - glib - Objects

So to fix your code example all you’d need to do is change

fn example(test: &ApplicationWindow) {
    test.connect_show_menubar_notify(move |_| {
        example(test);
    });
}

into

fn example(test: &ApplicationWindow) {
    let test_owned = test.clone();
    test.connect_show_menubar_notify(move |_| {
        example(&test_owned);
    });
}

There’s also the alternative possibility to have example take an owned ApplicationWindow in the first place

fn example(test: ApplicationWindow) {
    test.connect_show_menubar_notify(move |_| {
        example(test);
    });
}

which then means that the caller of example may need to clone the smart pointer / object here,
e.g. example(win.clone()); instead of example(&win));. I also suspect that there may be similar problems in your main function in the win.connect_add call which, as far as Google could tell me, might also require a : 'static closure.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.