rustlifetimeslint

Creating an Arc clone for a callback: `xxx` does not live long enough


As I am new to Rust, I have been reading about lifetimes and I am slowly wrapping my head around them, along with shared data using Arc and Mutex. I think that the way I am doing this is the correct approach.

If I understand the error correctly (based on what I have read), it is telling me that the callback may live longer than the variable b so it throws the error. What I am not sure of, is how do I resolve that error?

Note: AppWindow, AppMenu, and AppState are automatically generated by the slint library. However, their definitions are as follows:

Cargo.toml

[dependencies]
slint = "1.8.0"
use std::sync::Arc;

slint::slint! {
  import { Button } from "std-widgets.slint";
  export component AppWindow inherits Window {
    Button { text: "Example"; }
  }
  export global AppMenu {
    in-out property <bool> is-menu-opened: false;
  }
  export global AppState {
    callback close-tab(bool);
    callback active-project-changed(int);
    in-out property <int> active-project: 0;
  }
}

pub fn do_something(app_state: &AppState) {
  app_state.set_active_project(1);
}

fn main() -> Result<(), slint::PlatformError> {
  let app_window = Arc::new(AppWindow::new()?);

  let a = app_window.clone();
  let b = app_window.clone();
  let app_menu_original: Arc<AppMenu> = Arc::new(a.global::<AppMenu>());
  let app_state_original: Arc<AppState> = Arc::new(b.global::<AppState>()); // error is on this line

  /* ... My callbacks to hook into slint ... */

  let app_state = app_state_original.clone();
  app_state_original.clone().on_active_project_changed(move |index| {
    do_something(&app_state);
  });

  let app_state = app_state_original.clone();
  app_state_original.clone().on_close_tab(move |value| {
    do_something(&app_state);
  });

  /* ... The end of the program ... */

  app_window.clone().run()?;
  Ok(())
}
error[E0597]: `b` does not live long enough
   --> packages/app/main.rs:46:52
    |
44  |   let b = app_window.clone();
    |       - binding `b` declared here
45  |   let app_menu_original: Arc<AppMenu> = Arc::new(a.global::<AppMenu>());
46  |   let app_state_original: Arc<AppState> = Arc::new(b.global::<AppState>());
    |                                                    ^---------------------
    |                                                    |
    |                                                    borrowed value does not live long enough
    |                                                    argument requires that `b` is borrowed for `'static`
...
120 | }
    | - `b` dropped here while still borrowed

Solution

  • After looking at the docs, I found that there is a alternative way to access AppSate, and that is to use AppState::get(&app).

    So, instead of doing a bunch of Arc::clone()'s of AppState, in the callback I can use AppState::get(&app) like this (the get could probably be moved to the do_something() function):

      let app = app_window.clone();
      AppState::get(&app_window.clone()).on_active_project_changed(move |index| {
        let app_state = AppState::get(&app);
        do_something(&app_state);
      });
    

    This brings AppSate within the scope of the callback. The new code now looks like this:

    use std::sync::Arc;
    
    slint::slint! {
      import { Button } from "std-widgets.slint";
      export component AppWindow inherits Window {
        Button { text: "Example"; }
      }
      export global AppMenu {
        in-out property <bool> is-menu-opened: false;
      }
      export global AppState {
        callback close-tab(bool);
        callback active-project-changed(int);
        in-out property <int> active-project: 0;
      }
    }
    
    pub fn do_something(app_state: &AppState) {
      app_state.set_active_project(1);
    }
    
    fn main() -> Result<(), slint::PlatformError> {
      let app_window = Arc::new(AppWindow::new()?);
    
      /* ... My callbacks to hook into slint ... */
    
      let app = app_window.clone();
      AppState::get(&app_window.clone()).on_active_project_changed(move |index| {
        let app_state = AppState::get(&app);
        do_something(&app_state);
      });
    
      let app = app_window.clone();
      AppState::get(&app_window.clone()).on_close_tab(move |value| {
        let app_state = AppState::get(&app);
        do_something(&app_state);
      });
    
      /* ... The end of the program ... */
    
      app_window.clone().run()?;
      Ok(())
    }