Rust方法内&mut self生命周期问题:内部作用域赋值报错
WGPU + Winit v0.30 生命周期报错问题
问题描述
我正在结合wgpu与winit v0.30开发,按winit文档创建了窗口,但参考的learn wgpu教程用的是重构前的winit v0.29。作为Rust新手,入门书籍解决不了这个问题。
在主文件的if let语句中赋值self.state时,rust-analyzer提示生命周期不足,要求&mut self的生命周期'1必须长于'window。我懂生命周期基本概念,但不清楚结构体生命周期的工作机制,不知道报错原因。
代码展示
主文件代码
mod graphics; use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::window::{Window, WindowId}; use graphics::State; #[derive(Default)] struct App<'window> { window: Option<Window>, state: Option<State<'window>>, } impl<'window> ApplicationHandler for App<'window> { fn resumed(&mut self, event_loop: &ActiveEventLoop) { let window = event_loop .create_window(Window::default_attributes()) .unwrap(); self.window = Some(window); if let Some(win) = &self.window { self.state = Some(State::new(win)); } } fn window_event( &mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent, ) { } } fn main() { let event_loop = EventLoop::new().unwrap(); event_loop.set_control_flow(ControlFlow::Wait); let mut app = App::default(); event_loop.run_app(&mut app); }
graphics模块代码
use pollster::FutureExt as _; use wgpu::Backends; use wgpu::Instance; use wgpu::InstanceDescriptor; use winit::{event::WindowEvent, window::Window}; pub struct State<'window> { surface: wgpu::Surface<'window>, device: wgpu::Device, queue: wgpu::Queue, config: wgpu::SurfaceConfiguration, size: winit::dpi::PhysicalSize<u32>, // The window must be declared after the surface so // it gets dropped after it as the surface contains // unsafe references to the window's resources. window: &'window Window, }
报错原因
问题核心是生命周期绑定不匹配:
App<'window>的'window生命周期代表整个App实例的生命周期。- 在
resumed方法中,&self.window取到的是临时引用,生命周期仅局限于resumed方法执行期间(标记为'1),远短于App的'window生命周期。 - 编译器要求
State<'window>持有的&'window Window必须和App的'window生命周期一致,但你传入的是短生命周期的临时引用,因此触发报错。
另外,原learn wgpu教程的v0.29版本用的是旧事件循环模式,不需要处理这种生命周期绑定,这也是适配新winit版本时遇到问题的关键原因。
解决方案
推荐两种适合新手的修改方式:
方式1:用Arc规避生命周期问题
使用Arc共享窗口所有权,让State和App同时持有窗口的引用,彻底规避生命周期绑定问题:
- 修改主文件:
use std::sync::Arc; #[derive(Default)] struct App { window: Option<Arc<Window>>, state: Option<State>, } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { let window = Arc::new(event_loop .create_window(Window::default_attributes()) .unwrap()); self.window = Some(window.clone()); self.state = Some(State::new(window)); } // 其他方法保持不变 }
- 修改graphics模块的
State:
use std::sync::Arc; pub struct State { surface: wgpu::Surface<'static>, device: wgpu::Device, queue: wgpu::Queue, config: wgpu::SurfaceConfiguration, size: winit::dpi::PhysicalSize<u32>, window: Arc<Window>, } impl State { pub fn new(window: Arc<Window>) -> Self { let size = window.inner_size(); let instance = Instance::new(InstanceDescriptor { backends: Backends::all(), ..Default::default() }); let surface = unsafe { instance.create_surface(window.clone()) }.unwrap(); // 后续设备、队列、配置等初始化逻辑保持不变 Self { surface, device, queue, config, size, window, } } }
方式2:让State拥有Window所有权
修改State结构体直接持有Window的所有权,去掉生命周期参数:
// graphics模块的State修改后 pub struct State { surface: wgpu::Surface<'static>, device: wgpu::Device, queue: wgpu::Queue, config: wgpu::SurfaceConfiguration, size: winit::dpi::PhysicalSize<u32>, window: Window, } // 主文件的App和resumed方法修改 #[derive(Default)] struct App { state: Option<State>, } impl ApplicationHandler for App { fn resumed(&mut self, event_loop: &ActiveEventLoop) { let window = event_loop .create_window(Window::default_attributes()) .unwrap(); self.state = Some(State::new(window)); } // 其他方法保持不变 }
内容的提问来源于stack exchange,提问作者Pizza98
相关产品推荐
相关产品推荐

