You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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`

问题根源

  1. 多可变借用冲突:当前Rust借用检查器对数组索引的可变借用处理不够精细,循环中每次&mut frames[i]会被判定为对整个frames数组的可变借用,导致多次迭代时出现“同时存在多个可变借用”的错误。
  2. 自引用结构体的生命周期矛盾:BufferPool同时拥有frames(所有权)和指向它的引用(存在于frame_descriptors),这种自引用结构违反了Rust的生命周期规则——编译器无法保证返回结构体后,引用依然有效(尽管逻辑上两者绑定在一起)。
  3. 移动时存在活跃借用:将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 15:40:28