Using a variable from a higher scope in a callback

I am trying to use an object mechanics from the higher scope in a callback. To do that I tried to make object mechanics static.

pub fn main()
{
  let mut mechanics : &'static Mechanics = &'static mut Mechanics { val : 13. };
  let mut renderer = Render::new( "solution1".to_string() );
  renderer.f_on_update = Box::new( ||
  {
    println!( "mechanics.val : {}", mechanics.val )
  });
  renderer.update();
}

//

struct Mechanics
{
  val : f32,
}

//

struct Render
{
  name : String,
  f_on_update : Box< dyn Fn() >,
}

//

impl Render
{
  fn new( name : String ) -> Self
  {
    let f_on_update = || {};
    Self { name, f_on_update : Box::new( f_on_update ) }
  }
  fn update( &self )
  {
    (self.f_on_update)();
  }
}

But I get the error:

error: borrow expressions cannot be annotated with lifetimes
 --> src/static_solution_1.rs:4:44
  |
4 |   let mut mechanics : &'static Mechanics = &'static mut Mechanics { val : 13. };
  |                                            ^-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |                                             |
  |                                             annotated with lifetime here
  |                                             help: remove the lifetime annotation

Changing to

let mut mechanics : &'static Mechanics = &Mechanics { val : 13. };

Gives the error:

error[E0597]: `mechanics` does not live long enough
  --> src/static_solution_1.rs:8:37
   |
6  |     renderer.f_on_update = Box::new( ||
   |                            -         -- value captured here
   |  __________________________|
   | |
7  | |   {
8  | |     println!( "mechanics.val : {}", mechanics.val )
   | |                                     ^^^^^^^^^ borrowed value does not live long enough
9  | |   });
   | |____- cast requires that `mechanics` is borrowed for `'static`
10 |     renderer.update();
11 |   }
   |   - `mechanics` dropped here while still borrowed

I do something wrong but have no clue where to look for an answer. Reading about static lifetime didn't help me to overcome the obstacle. Any suggestion?

Is it possible to move the mechanics? If it is then using move closure should solve the problem, i.e.

  let mechanics  = Mechanics { val : 13. };
  ...
  renderer.f_on_update = Box::new(move ||
  {
    ...
  });

Otherwise, I think you should also wrap mechanics with Box

1 Like

You can't make things 'static by lifetime annotation. Lifetimes describe what things already are, and don't tell them what they are supposed to be. Annotations are assertions, not commands.

For a thing to be 'static, it has to be either a compile-time constant hardcoded in the executable, or leaked memory. There is no other way to make something 'static.

When you see the compiler demanding 'static, it's trying to tell you that references are forbidden, and the code just won't work with local variables. In such case you need move || closures and/or wrap data in Arc if you need to reference it from multiple places.

6 Likes

As zynaxsoft and kornel said the proper solution is using Box and keyword move to make variables available from the callback. Some reading about a closure explaining that.

Full solution:


fn main()
{
  let mut mechanics = Box::new( Mechanics { val : 13. } );
  let mut renderer = Render::new( "solution1".to_string() );
  renderer.f_on_update = Box::new( move ||
  {
    println!( "mechanics.val : {}", mechanics.val );
  });
  renderer.update();
}

//

struct Mechanics
{
  val : f32,
}

//

struct Render
{
  name : String,
  f_on_update : Box< dyn Fn() >,
}

//

impl Render
{
  fn new( name : String ) -> Self
  {
    let f_on_update = || {};
    Self { name, f_on_update : Box::new( f_on_update ) }
  }
  fn update( &self )
  {
    (self.f_on_update)();
  }
}

And a playground with the solution.

Thank you, @kornel and @zynaxsoft for help.

1 Like

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.