Referencing self from within closure in a method

I am fairly new to rust and I am trying to understand how best to handle the following situation as I am currently getting a lifetime error:

closure requires unique access to 'self' but 'self.events_loop' is already borrowed

So, given some struct:

pub struct Window {
   events_loop: winit::EventsLoop,
   child: Renderer
}

How would I call render in the following start methods event_loop closure?

impl Window {
   pub fn new(window_options: WindowOptions) -> Window {...}

   pub fn render(&mut self) {
     self.child.render();
   }
   
   pub fn start(&mut self) {
     let events_loop = &mut self.events_loop;
     events_loop.run_forever(|global_event| {
       ...
       self.render();
       ...
     });
   }
}

The error is exactly what it says.

pub fn render(&mut self)

&mut self means that it takes exclusive control of the whole self object. Rust doesn't check function bodies, so it doesn't matter that you only use child inside. If the function signature says all of self, then it requires all of self.

And when this method is called there can't be anything anywhere else holding any other reference to self. However, there's events_loop variable that is borrowed from self, so it locked it for its own exclusive use.

If you think of &mut as an exclusive write lock, then you have a deadlock from locks on events_loop and render.

I think I understand the problem, but this is such a common idiom coming from garbage collected languages that I'm having trouble crafting a solution. Any help or advice with an alternative approach would be appreciated.

  • Don't use methods. Instead of &mut self make an associated function that takes child only.

  • Separate objects out. Don't put events_loop in the Window, but pass it separately.

  • Use interior mutability. Instead of &mut, use shared & references and RefCell that allows to "cheat" and get &mut out of an "immutable" reference.

  • Rc<RefCell<T>> or Arc<Mutex<T>> are commonly used to get all the freedoms of a GC language. They add some syntax noise, because Rust likes to be explicit about such things.

2 Likes