Wrapping a winit window with a struct

I'm trying to write some abstractions on top of a winit window and I've run into a problem.

pub struct Window {
    event_loop: EventLoop<()>,
    window: winit::window::Window
}

impl Window {
    pub fn new() -> Window {
        let event_loop = EventLoop::new();
        let window = WindowBuilder::new().build(&event_loop).unwrap();

        return Window { event_loop, window }
    }

    pub fn run(mut self) {
        self.event_loop.run(move |event, _, control_flow| {
            *control_flow = ControlFlow::Poll;
    
            match event {
                Event::WindowEvent {
                    event: WindowEvent::CloseRequested,
                    ..
                } => {
                    println!("The close button was pressed; stopping");
                    *control_flow = ControlFlow::Exit
                },
                Event::MainEventsCleared => {
                    self.window.request_redraw();
                },
                Event::RedrawRequested(_) => {
                    
                },
                _ => ()
                }
            });
    
    }
}

However, this fails with the error message "cannot move out of self.event_loop which is behind a mutable reference". I can't seem to find another way to access self from within the closure. Any help is appreciated.

1 Like

As far as I can tell, self.event_loop.run(...) will borrow event_loop, and move |...| { ... self ... } will try to move self into the callback, which fails as it isn't safe to move something that is borrowed.

I think the fix is to explicily reference self.window, so that the reference can be moved instead of self. To do that, before the run line, take a reference to the window:

    let window = &mut self.window;

then inside the callback, use the reference instead of self:

    window.request_redraw();

This is safe, as it is OK to lend out references to separate parts of self at the same time.

Unfortunately this does not work, with the error message "borrowed value does not live long enough", in reference to &mut self.window. Reading through the winit documentation, the closure passed to event_loop.run() has a lifetime of 'static, and this is (I think) the source of the problems when it comes to trying to move values into the closure. I tried to make event_loop and window static, but I probably did so incorrectly as I got many errors.

When the compiler says something should be static, the solutions is never to add 'static in random places. You have to fix your ownership such that things are owned in a single place. If the closure wants ownership, the run function can't also retain ownership for accessing the event loop.

One option is to take the event loop away from self, so you can give ownership of self to the closure while retaining ownership of the event loop in run. Another option is to share the event loop with an Rc, although if run takes &mut self, that wont work because &mut self requires exclusive access, which is at odds with sharing it using an Rc.

Wrapping event_loop with an Rc gives the error message cannot move out of an Rc when trying to call self.event_loop.run(). The winit run() function is defined as

pub fn run<F>(self, event_handler: F) -> !
    where
        F: 'static + FnMut(Event<'_, T>, &EventLoopWindowTarget<T>, &mut ControlFlow),

which takes only self. I can't think of a convenient way to separate event_loop from self.

Well it sounds like you should separate it from self. One option is to put it in an Option and use take

// If event_loop is an Option
let handler = self.event_loop.take().unwrap();
2 Likes

Works perfectly, thanks!