Borrowed Data Escapes Outside of Associated function with winit loop

fn run_windowed(&mut self) -> Option<TreeAppOutput>{

        let update_schedule = &self.update_scedule;
        let frame_schedule = self.frame_scedule.as_ref().unwrap();
        let world = self.world.as_mut();


        let (queue, device, physical_device, surface, event_loop, memory_allocator) = base_graphics_setup("branch render example".to_string());
        let (mut swapchain, swapchain_images) = get_swapchain(&physical_device, &surface, &device);
        let render_pass = (self.render_pass_fn.unwrap())(&device, &swapchain);
        let (mut framebuffers, window_dimensions) = get_framebuffers(&memory_allocator, &swapchain_images, &render_pass);


        let mut used_passes = 1;
        let (terrain_subpass, terrain_pipeline) = {
            if self.do_terrain {
                used_passes += 1;
                (Some(Subpass::from(render_pass.clone(), used_passes)).unwrap(), Some(get_terrain_pipeline(window_dimensions, &device, &render_pass)))
            }
            else {(None, None)}
        };

        let (mut gui, do_gui) = {
            if self.do_gui {

                let subpass = Subpass::from(render_pass.clone(), used_passes).unwrap();
                (Some(create_gui_from_subpass(&event_loop, &surface, &queue, &subpass)), true)
            }
            else {(None, false)}
        };

        let mut camera = Camera::new(Some(self.camera_state.0), Some(self.camera_state.1), None, None);


        // this determines if the swapchain needs to be rebuilt, happens when the window dimensions change
        let mut recreate_swapchain = false;

        let mut last_frame_time = Instant::now();
        
        event_loop.run(move |event, _, control_flow| {
            match event {
                
                Event::WindowEvent { window_id: _, event } => {
                    // pass things to gui
                    if do_gui {
                        pass_winit_event_to_gui(&mut gui.as_mut().unwrap(), &event);
                    }
                    // check for resize or close
                    match event {
                        WindowEvent::Resized(_) => {
                            recreate_swapchain = true
                        }
                        WindowEvent::CloseRequested => {
                            *control_flow = ControlFlow::Exit;
                        }
                        WindowEvent::KeyboardInput {
                            input:winit::event::KeyboardInput {
                                virtual_keycode: Some(keycode),
                                state,
                                ..
                            },
                            .. 
                        } => {
                            camera.process_key(keycode, state == ElementState::Pressed);
                        }
                        _ => (),
                    }
                },


                Event::MainEventsCleared => {
                    // fixed schedules
                    let delta_time = last_frame_time.elapsed();
                    update_schedule.run(world, delta_time);
                    camera.do_move(delta_time);
                    last_frame_time = Instant::now();
                    

                    // schedules
                    if do_gui {
                        run_gui_commands(world, gui.as_mut().unwrap());
                    }
                    frame_schedule.run(world);
                }

                _ => (),
            }
        });
    }

self.world is a box containing a World struct from Bevy ECS, the compiler says that borrowed data escapes outside the associated fn when the code passing world into functions is added, but world is a mutable reference so surely the data isn't leaving, no data is being moved, only referenced so I'm not sure how to fix the error.

A general best practice if you have a compilation error, especially on an non-standalone code sample that cannot be compiled on its own, is to include the error message with your question. It’s no fun playing compiler as someone trying to answer a question, even less so if the code is long and involved lots of usage of unknown types and API. On that note, besides posting the full error message (i.e. as given not by your IDE but in the style of cargo check or cargo build output in the terminal), you might need to give us function signatures of APIs used in the erroneous code section the compiler points to.

In this case, it looks like the type of event_loop, and the signature of its run method might be relevant, so you might want to give us more information on that. If those were to have a 'static bound on the received closure – possibly because the look is run concurrently – that could explain why you get an error, as references like world or update_schedule or frame_schedule would not fulfill such a bound, with their lifetimes likely being bound to the &mut self borrow’s lifetime. Discussing how the problem could then be addressed makes more sense after we can confirm to have identified the problem correctly ^^

4 Likes

Ah okay sorry,

The full compiler error is:

error[E0521]: borrowed data escapes outside of associated function
   --> src/app.rs:168:9
    |
129 |       fn run_windowed(&mut self) -> Option<TreeAppOutput>{
    |                       ---------
    |                       |
    |                       `self` is a reference that is only valid in the associated function body
    |                       let's call the lifetime of this reference `'1`
...
168 | /         event_loop.run(move |event, _, control_flow| {
169 | |             match event {
170 | |                 
171 | |                 Event::WindowEvent { window_id: _, event } => {
...   |
215 | |             }
216 | |         });
    | |          ^
    | |          |
    | |__________`self` escapes the associated function body here
    |            argument requires that `'1` must outlive `'static`

event_loop is a winit EventLoop and the run command's signature looks like this:

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

I think it may be to do with the static lifetimes but I'm not sure how to work around this

I'm not really familiar with winit, but it looks like, perhaps, this run function is not returning, so you won't be calling run_windowed multiple times on the same value, I guess? In that case, you could turn it into a (self) method, as opposed to (&mut self), which avoids the lifetimes attached to self and thus might solve the problem of the closure being non-'static, at least with a few further adjustments: You would also need to avoid creating references such as update_schedule, frame_schedule, and world before self will be moved into the closure, and instead you could try to create those references when they are really needed, inside of the event loop. (This will create them repeatedly, but at least judging by the method names, it looks like these are rather trivial field-accesses and comparable operations, anyways.)

E.g. a change like

- fn run_windowed(&mut self) -> Option<TreeAppOutput>{
+ fn run_windowed(self) -> Option<TreeAppOutput>{

-       let update_schedule = &self.update_scedule;
-       let frame_schedule = self.frame_scedule.as_ref().unwrap();
-       let world = self.world.as_mut();


        let (queue, device, physical_device, surface, event_loop, memory_allocator) = base_graphics_setup("branch render example".to_string());
        ………
        ………
        let mut last_frame_time = Instant::now();
        
        event_loop.run(move |event, _, control_flow| {
+           let update_schedule = &self.update_scedule;
+           let frame_schedule = self.frame_scedule.as_ref().unwrap();
+           let world = self.world.as_mut();
            match event {
                ………
                ………
            }
        });
    }

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.