Rust中如何强制一个结构体生命周期长于另一个结构体
我定义了两个结构体:
- 第一个为「父(parent)」结构体,持有需要显式清理的资源,我通过为其实现
Droptrait处理资源清理逻辑。 - 第二个为「子(child)」结构体,从父结构体获取,仅当父级持有的资源未被释放时才具备有效性。
换言之,所有父结构体实例的生命周期必须长于其对应的子结构体实例。
我最初的方案是通过引用让借用检查器强制校验该规则,实现代码如下:
struct Parent { // Private resource data. } impl Parent { fn new() -> Self { Parent {} } fn new_child(&self) -> Child { return Child { _parent: self }; } } impl Drop for Parent { fn drop(&mut self) { // 清理逻辑执行后,所有Child实例都会失效 } } struct Child<'a> { _parent: &'a Parent, // 私有数据字段 } impl<'a> Child<'a> { fn hello(&self) { println!("Hello Child!"); } } fn main() { let parent = Parent::new(); let child = parent.new_child(); child.hello(); // 取消下面两行注释会编译失败,符合预期 // drop(parent); // child.hello(); }
该方案可正常运行,但Child结构体实际上无需感知父结构体的存在。我考虑将引用字段替换为PhantomData,修改后的结构体定义如下:
struct Child<'a> { _parent: PhantomData<&'a Parent>, // 私有数据字段 }
但在该实现下,我要如何在Parent::new_child方法中将_parent字段与对应的父结构体实例绑定,以保证生命周期约束正常生效?
直接在构造Child实例时给_parent字段赋值PhantomData就行,生命周期会自动和传入的&self绑定,不需要额外做特殊绑定操作。
首先在文件顶部引入PhantomData:
use std::marker::PhantomData;
然后把Parent的new_child方法改成这样:
fn new_child(&self) -> Child<'_> { Child { _parent: PhantomData, // 其余私有字段按正常逻辑初始化 } }
原理很简单:PhantomData<T>运行时完全不占内存,唯一作用就是给编译器传递类型、生命周期的约束信息。new_child的方法签名已经约定了返回的Child生命周期和&self(也就是当前Parent实例的借用生命周期)对齐,只要构造Child时给_parent填上对应类型的PhantomData实例,编译器就会自动把生命周期约束加上,和之前存真实引用的校验效果完全一致:如果Parent提前被drop,对应的Child实例还被使用的话,编译直接不通过。
提个注意点:这种写法和存真实
&'a Parent引用的借用约束有细微差别:存真实引用时,整个'a生命周期内都会持有Parent的不可变借用,这段时间你没法对Parent做可变操作;换成PhantomData<&'a Parent>之后,编译器只会校验两者的生命周期长短关系,不会给Parent加借用锁,如果Child存活期间你需要修改Parent的内容,这种写法会更灵活,只要你自己保证修改Parent不会导致关联的Child失效即可。
内容的提问来源于stack exchange,提问作者Debaug

