Rust:如何在Struct::new()中为结构体成员创建另一成员的可变引用?
BufferPool初始化中引用关联的编译问题解决
问题背景
尝试在BufferPool::new()方法中,将分配得到的frames对象的可变引用关联到同一结构体的frame_descriptors字段时,出现多个编译错误,无法完成初始化。
相关代码
const PAGE_SIZE: usize = 4096; const NUM_FRAMES: usize = 1000; pub struct Frame { pub data: [u8; PAGE_SIZE], } impl Default for Frame { fn default() -> Self { Self { data: [0; PAGE_SIZE] } } } pub struct FrameDescriptor<'bp> { pub frame: &'bp mut Frame, pub is_dirty: bool, } impl Default for FrameDescriptor<'_> { fn default() -> Self { let frame_ptr: *mut Frame = std::ptr::null_mut(); let frame_ref: &mut Frame = unsafe { &mut *frame_ptr }; Self { frame: frame_ref, is_dirty: false } } } pub struct BufferPool<'bp> { pub frames: Box<[Frame; NUM_FRAMES]>, pub frame_descriptors: Box<[FrameDescriptor<'bp>; NUM_FRAMES]>, } // ----> ISSUE OCCURS HERE, IN NEW <----- impl BufferPool<'_> { pub fn new() -> Self { let mut frames: Box<[Frame; NUM_FRAMES]> = Box::new(core::array::from_fn(|_| Default::default())); let mut frame_descriptors: Box<[FrameDescriptor; NUM_FRAMES]> = Box::new(core::array::from_fn(|_| Default::default())); for i in 0..NUM_FRAMES { frame_descriptors[i].frame = &mut frames[i]; } Self { frames, frame_descriptors } } }
编译错误信息
error[E0499]: cannot borrow `frames[_]` as mutable more than once at a time --> src/main.rs:197:42 | 189 | pub fn new() -> Self { | ---- return type is BufferPool<'1> ... 197 | frame_descriptors[i].frame = &mut frames[i]; | ^^^^^^^^^^^^^^ `frames[_]` was mutably borrowed here in the previous iteration of the loop ... 200 | / Self { 201 | | frames, 202 | | frame_descriptors, 203 | | free_list: (0..BUF_POOL_NUM_FRAMES).collect(), ... | 206 | | clock_hand: 0, 207 | | } | |_________- returning this value requires that `frames[_]` is borrowed for `'1` error[E0515]: cannot return value referencing local data `frames[_]` --> src/main.rs:200:9 | 197 | frame_descriptors[i].frame = &mut frames[i]; | -------------- `frames[_]` is borrowed here ... 200 | / Self { 201 | | frames, 202 | | frame_descriptors, 203 | | free_list: (0..BUF_POOL_NUM_FRAMES).collect(), ... | 206 | | clock_hand: 0, 207 | | } | |_________^ returns a value referencing data owned by the current function error[E0505]: cannot move out of `frames` because it is borrowed --> src/main.rs:201:13 | 189 | pub fn new() -> Self { | ---- return type is BufferPool<'1> ... 197 | frame_descriptors[i].frame = &mut frames[i]; | -------------- borrow of `frames[_]` occurs here ... 200 | / Self { 201 | | frames, | | ^^^^^^ move out of `frames` occurs here 202 | | frame_descriptors, 203 | | free_list: (0..BUF_POOL_NUM_FRAMES).collect(), ... | 206 | | clock_hand: 0, 207 | | } | |_________- returning this value requires that `frames[_]` is borrowed for `'1`
问题根源
- 多可变借用冲突:当前Rust借用检查器对数组索引的可变借用处理不够精细,循环中每次
&mut frames[i]会被判定为对整个frames数组的可变借用,导致多次迭代时出现“同时存在多个可变借用”的错误。 - 自引用结构体的生命周期矛盾:
BufferPool同时拥有frames(所有权)和指向它的引用(存在于frame_descriptors),这种自引用结构违反了Rust的生命周期规则——编译器无法保证返回结构体后,引用依然有效(尽管逻辑上两者绑定在一起)。 - 移动时存在活跃借用:将
frames移入返回的BufferPool时,frame_descriptors中的引用仍在借用frames,违反了“移动值时不能有活跃借用”的规则。
解决方案
方案一:使用裸指针替代引用(unsafe)
通过裸指针绕过Rust的引用规则,手动保证内存安全:
const PAGE_SIZE: usize = 4096; const NUM_FRAMES: usize = 1000; pub struct Frame { pub data: [u8; PAGE_SIZE], } impl Default for Frame { fn default() -> Self { Self { data: [0; PAGE_SIZE] } } } // 用裸指针代替可变引用,去掉生命周期参数 pub struct FrameDescriptor { pub frame: *mut Frame, pub is_dirty: bool, } impl Default for FrameDescriptor { fn default() -> Self { Self { frame: std::ptr::null_mut(), is_dirty: false } } } // BufferPool不再需要生命周期参数 pub struct BufferPool { pub frames: Box<[Frame; NUM_FRAMES]>, pub frame_descriptors: Box<[FrameDescriptor; NUM_FRAMES]>, } impl BufferPool { pub fn new() -> Self { let mut frames: Box<[Frame; NUM_FRAMES]> = Box::new(core::array::from_fn(|_| Default::default())); let mut frame_descriptors: Box<[FrameDescriptor; NUM_FRAMES]> = Box::new(core::array::from_fn(|_| Default::default())); for i in 0..NUM_FRAMES { // 将可变引用转换为裸指针 frame_descriptors[i].frame = &mut frames[i] as *mut Frame; } Self { frames, frame_descriptors } } // 使用时需要unsafe将指针转回引用,确保BufferPool仍有效 pub unsafe fn get_frame_mut(&mut self, desc: &mut FrameDescriptor) -> &mut Frame { &mut *desc.frame } }
方案二:使用索引替代引用(完全安全)
调整结构体设计,让FrameDescriptor存储frames数组的索引,而非直接引用:
const PAGE_SIZE: usize = 4096; const NUM_FRAMES: usize = 1000; pub struct Frame { pub data: [u8; PAGE_SIZE], } impl Default for Frame { fn default() -> Self { Self { data: [0; PAGE_SIZE] } } } pub struct FrameDescriptor { pub frame_index: usize, pub is_dirty: bool, } impl Default for FrameDescriptor { fn default() -> Self { Self { frame_index: 0, is_dirty: false } } } pub struct BufferPool { pub frames: Box<[Frame; NUM_FRAMES]>, pub frame_descriptors: Box<[FrameDescriptor; NUM_FRAMES]>, } impl BufferPool { pub fn new() -> Self { let frames: Box<[Frame; NUM_FRAMES]> = Box::new(core::array::from_fn(|_| Default::default())); let frame_descriptors: Box<[FrameDescriptor; NUM_FRAMES]> = Box::new(core::array::from_fn(|i| FrameDescriptor { frame_index: i, is_dirty: false })); Self { frames, frame_descriptors } } // 通过索引获取可变引用,完全安全 pub fn get_frame_mut(&mut self, desc: &FrameDescriptor) -> &mut Frame { &mut self.frames[desc.frame_index] } }
说明
方案二是更推荐的安全做法,完全规避了自引用问题,不需要unsafe代码。方案一适合必须直接持有指针的场景,但需要开发者手动保证指针的有效性(只要BufferPool未被销毁,frames就不会被释放,因此是安全的)。
内容的提问来源于stack exchange,提问作者Gavin Ray
相关产品推荐
相关产品推荐

