如何存储生命周期不超过其容器的Rust引用?
问题分析与解决方案
编译错误的核心原因很明确:set_i方法把self的可变引用生命周期强行绑定到了ShallowArray的泛型生命周期'a,导致调用该方法后,arr会被持续可变借用直到'a结束(也就是arr1的生命周期,到test函数末尾)。但函数末尾要执行ShallowArray的Drop逻辑,此时arr还处于被借用状态——Rust的借用检查器不允许这种冲突,因为Drop会获取&mut self,和已有的可变借用互斥。
修复步骤
1. 修正set_i方法的生命周期签名
把原来的fn set_i(&'a mut self, idx: usize, obj: &'a Array)改成fn set_i(&mut self, idx: usize, obj: &'a Array)。去掉self引用上的'a绑定,只保留参数obj的'a生命周期约束:既保证存储的引用生命周期不超过容器,又不会把self的借用锁死到'a。
2. 修复后完整代码
use std::alloc::{alloc, dealloc, Layout}; use std::marker::PhantomData; use std::mem::ManuallyDrop; #[repr(C)] struct Array { size: usize, arr: *mut Array, } // Drop for Array deallocates array recursively impl Drop for Array { fn drop(&mut self) { if self.size > 0 && !self.arr.is_null() { let layout = Layout::array::<Array>(self.size).unwrap(); unsafe { for i in 0..self.size { std::ptr::drop_in_place(self.arr.add(i)); } dealloc(self.arr as *mut u8, layout); } } } } // must have same representation as Array #[repr(C)] struct ShallowArray<'a> { inner: ManuallyDrop<Array>, _phantom: PhantomData<&'a Array>, } impl<'a> ShallowArray<'a> { fn new(size: usize) -> Self { let layout = Layout::array::<Array>(size).unwrap(); let arr = unsafe { alloc(layout) } as *mut Array; unsafe { std::ptr::write_bytes(arr, 0, layout.size()); } Self { inner: ManuallyDrop::new(Array { size, arr }), _phantom: PhantomData, } } // 去掉self引用上的'a绑定 fn set_i(&mut self, idx: usize, obj: &'a Array) { if idx >= self.inner.size { panic!("Index out of bounds"); } unsafe { *self.inner.arr.add(idx) = Array { size: obj.size, arr: obj.arr, }; } } } impl Drop for ShallowArray<'_> { fn drop(&mut self) { // deallocate only the first level let layout = Layout::array::<Array>(self.inner.size).unwrap(); unsafe { dealloc(self.inner.arr as *mut u8, layout); } } } fn test() { let arr1 = Array { size: 0, arr: std::ptr::null_mut(), }; let mut arr = ShallowArray::new(3); arr.set_i(0, &arr1); } fn main() { test(); }
关键逻辑说明
ShallowArray<'a>的PhantomData<&'a Array>用来告诉编译器:结构体持有生命周期为'a的Array引用(内部裸指针通过PhantomData标注生命周期约束),确保存储的指针指向的Array至少和ShallowArray存活时间一致。set_i方法只约束obj的生命周期为'a,不需要绑定self的引用,这样调用方法后self的可变借用会立即释放,不会干扰后续的Drop执行。ManuallyDrop<Array>用来避免触发Array的递归Drop逻辑,因为ShallowArray只负责释放自己分配的第一层数组,元素的释放由原Array自行处理。
内容的提问来源于stack exchange,提问作者Artefacto
相关产品推荐
相关产品推荐

