Rust中App结构体引用自身字段的生命周期报错求助
Rust自引用结构体的生命周期问题解决办法
问题场景
我定义了一个App结构体,里面有一个拥有所有权的window字段(简化为u32类型),还有一个需要引用这个window的State结构体。但在resumed方法里填充State时,编译器报错,说无法保证self的生命周期'1比引用的生命周期'a长。我觉得'1拥有window,引用应该和'1同生命周期,作为Rust新手,想要通俗易懂的解决办法。
原代码示例
struct State<'a> { window: &'a u32 } impl<'a> State<'a> { fn new(window: &'a u32) -> Self { State{ window } } } #[derive(Default)] struct App<'a> { window: u32, state: Option<State<'a>>, } trait ApplicationHandler { fn resumed(&mut self); } impl<'a> ApplicationHandler for App<'a> { fn resumed(&mut self) { self.window = 21; self.state = Some(State::new(&self.window)) } } fn main() { let mut app = App::default(); app.resumed(); }
编译错误
error: lifetime may not live long enough --> <source>:31:9 | 27 | impl<'a> ApplicationHandler for App<'a> { | -- lifetime `'a` defined here 28 | 29 | fn resumed(&mut self) { | - let's call the lifetime of this reference `'1` 30 | self.window = 21; 31 | self.state = Some(State::new(&self.window)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'a` error: aborting due to 1 previous error
问题原因
Rust编译器不允许结构体内部的字段引用同一个结构体的其他字段(也就是“自引用”),因为这会破坏Rust的借用规则:
- 当你修改
window时,state里的引用会变成悬垂引用(指向已经无效的内存)。 - 编译器无法跟踪这种内部引用的生命周期——
App的生命周期'a是结构体本身的生命周期,而resumed方法里&mut self的生命周期'1只是方法调用的临时周期,编译器没法保证'1能覆盖'a的全程。
解决办法
方法1:让State拥有window的所有权(最简单)
如果State不需要和App共享window,直接把window的所有权转给State就行,彻底消除引用和生命周期问题。
修改后的代码:
struct State { window: u32 } impl State { fn new(window: u32) -> Self { State{ window } } } #[derive(Default)] struct App { window: u32, state: Option<State>, } trait ApplicationHandler { fn resumed(&mut self); } impl ApplicationHandler for App { fn resumed(&mut self) { self.window = 21; self.state = Some(State::new(self.window)) } } fn main() { let mut app = App::default(); app.resumed(); }
方法2:用引用计数共享所有权(需要共享window时)
如果App和State都需要访问window,可以用Rc(单线程)或Arc(多线程)来共享所有权,这样两者都持有window的引用计数指针,不存在生命周期冲突。
不可修改window的情况
use std::rc::Rc; struct State { window: Rc<u32> } impl State { fn new(window: Rc<u32>) -> Self { State{ window } } } #[derive(Default)] struct App { window: Rc<u32>, state: Option<State>, } trait ApplicationHandler { fn resumed(&mut self); } impl ApplicationHandler for App { fn resumed(&mut self) { // Rc默认不可变,要修改值需要用Rc::get_mut(仅当只有一个引用时可用) *Rc::get_mut(&mut self.window).unwrap() = 21; self.state = Some(State::new(Rc::clone(&self.window))) } } fn main() { let mut app = App { window: Rc::new(0), state: None, }; app.resumed(); }
需要修改window的情况
如果要随时修改window的值,需要配合RefCell(单线程)或Mutex(多线程)来实现内部可变性:
use std::rc::Rc; use std::cell::RefCell; struct State { window: Rc<RefCell<u32>> } impl State { fn new(window: Rc<RefCell<u32>>) -> Self { State{ window } } } #[derive(Default)] struct App { window: Rc<RefCell<u32>>, state: Option<State>, } trait ApplicationHandler { fn resumed(&mut self); } impl ApplicationHandler for App { fn resumed(&mut self) { // 用borrow_mut()获取可变引用,修改值 *self.window.borrow_mut() = 21; // 克隆Rc指针给State self.state = Some(State::new(Rc::clone(&self.window))) } } fn main() { let mut app = App { window: Rc::new(RefCell::new(0)), state: None, }; app.resumed(); // 访问值需要用borrow()获取不可变引用 println!("App的window值:{}", *app.window.borrow()); println!("State的window值:{}", *app.state.as_ref().unwrap().window.borrow()); }
方法3:用自引用库(进阶)
如果一定要保留原始的自引用结构,可以用专门的Rust库(比如ouroboros)来处理,但作为新手不优先推荐,因为会增加依赖,且需要理解库的用法。
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

