How to handle borrowed value in a safe way?

Hello

I try to convert this example to a struct/impl:
https://github.com/gtk-rs/examples/blob/master/src/bin/basic.rs

extern crate gio;
extern crate gtk;

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

pub struct MyApp {
    app: gtk::Application
}

impl MyApp {

    pub fn new() -> MyApp {
        let app = gtk::Application::new("com.github.gtk-rs.examples.basic",
                                            Default::default())
.expect("Initialization failed...");
       let  my = MyApp{app: app};
        my.app.connect_activate(|app| {
                my.build_ui(app);
        });
        return my;
    }
    
    pub fn run(&self, args: &[String]) {
        self.app.run(args);
    }
    
    fn build_ui(&self, application: &gtk::Application) {
        let window = gtk::ApplicationWindow::new(application);
        window.set_title("First GTK+ Program");
        window.show_all();
    }

}


use std::env::args;

fn main() {
    println!("Hello, world!");
    let app = MyApp::new();
    app.run(&args().collect::<Vec<_>>())
}

and it complain on:

error[E0373]: closure may outlive the current function, but it borrows `my`, which is owned by the current function
  --> src/main.rs:61:33
   |
61 |         my.app.connect_activate(|app| {
   |                                 ^^^^^ may outlive borrowed value `my`
62 |                 my.build_ui(app);
   |                 -- `my` is borrowed here
note: function requires argument type to outlive `'static`
  --> src/main.rs:61:9
   |
61 | /         my.app.connect_activate(|app| {
62 | |                 my.build_ui(app);
63 | |         });
   | |__________^
help: to force the closure to take ownership of `my` (and any other referenced variables), use the `move` keyword
   |
61 |         my.app.connect_activate(move |app| {
   |                                 ^^^^^^^^^^

error[E0505]: cannot move out of `my` because it is borrowed
  --> src/main.rs:64:16
   |
61 |           my.app.connect_activate(|app| {
   |           -                       ----- borrow of `my` occurs here
   |  _________|
   | |
62 | |                 my.build_ui(app);
   | |                 -- borrow occurs due to use in closure
63 | |         });
   | |__________- argument requires that `my` is borrowed for `'static`
64 |           return my;
   |                  ^^ move out of `my` occurs here

error: aborting due to 2 previous errors

I also try to use the help suggest and change to:

my.app.connect_activate(move |app| {
                my.build_ui(app);
        });

But then I got:

error[E0505]: cannot move out of `my` because it is borrowed
  --> src/main.rs:61:33
   |
61 |         my.app.connect_activate(move |app| {
   |         ------ ---------------- ^^^^^^^^^^ move out of `my` occurs here
   |         |      |
   |         |      borrow later used by call
   |         borrow of `my.app` occurs here
62 |                 my.build_ui(app);
   |                 -- move occurs due to use in closure

error[E0382]: use of moved value: `my`
  --> src/main.rs:64:16
   |
61 |         my.app.connect_activate(move |app| {
   |                                 ---------- value moved into closure here
62 |                 my.build_ui(app);
   |                 -- variable moved due to use in closure
63 |         });
64 |         return my;
   |                ^^ value used here after move
   |
   = note: move occurs because `my` has type `MyApp`, which does not implement the `Copy` trait

error: aborting due to 2 previous errors

Thanks

Hi! So I think the problem is that you are trying to pass the object into the closure.

In your new() function, you initialize MyApp struct with the gtk::Application, then access the gtk::Application through the struct via the method build_ui(), which takes a &self. Rust does not like this very much (read: at all), it has a hard time figuring out ownership and borrowing if the struct start refering to itself.

If you remove the build_ui() member function, and do everything in the closure itself, it will run:

my.app.connect_activate(|app| {
    let window = gtk::ApplicationWindow::new(app);
    window.set_title("First GTK+ Program");
    window.show_all();
});

This is because the MyApp struct is no longer being borrowed twice (once by the connect_activate() function, once by the build_ui() method).

The original code has this as a separate function outside of any struct, that would work as well.

Good luck on your Rust adventure!

Thanks, and I could do this:

pub fn new() -> MyApp {
        let app = gtk::Application::new("com.github.gtk-rs.examples.basic",
                                            Default::default())
.expect("Initialization failed...");
       let  my = MyApp{app: app};
        my.build_ui();
        return my;
    }
fn build_ui(&self) {
        self.app.connect_activate(|app| {
            let window = gtk::ApplicationWindow::new(app);
            window.set_title("First GTK+ Program");
            window.show_all();
        });

You don't have to use return explicitly in Rust. You can implicitly return the last expression in a block if you don't put a semicolon after it. Your new() function's last line would then become:

my

and the result will be identical.

1 Like

Thanks.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.