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

Rust ECS游戏引擎中Controller组件更新的可变借用冲突问题及优化方案问询

Rust ECS游戏引擎中Controller组件更新的可变借用冲突问题及优化方案问询

我正在用Rust开发一款ECS架构的游戏引擎,目前遇到了一个可变借用的冲突问题,想和大家探讨下有没有更优雅的解决思路。

项目背景

我的每个场景(Scene)结构如下:

pub struct Scene {
    pub entities_index: usize,
    pub component_map: HashMap<TypeId, Box<dyn ComponentVec>>,
}

组件分为两种形式:一种是基础结构体,比如Transform组件:

pub struct TransformComponent {
    pub transform: Mat4,
    children: Vec<usize>,
}

另一种是 trait 对象,比如Controller组件,它需要在帧更新时能拿到自身索引和可变的场景引用,以便修改其他组件:

pub trait Controller{
    fn update(&mut self, index: usize, scene: &mut Scene);
}

目前我用泛型方法来获取动态组件的可变引用,这个逻辑是正常工作的:

pub fn borrow_component_vec_mut<ComponentType: 'static>(
        &self,
    ) -> Option<RefMut<Vec<Option<ComponentType>>>> {
        if let Some(component_vec) = self.component_map.get(&TypeId::of::<ComponentType>()) {
        if let Some(component_vec) = component_vec
            .as_any()
            .downcast_ref::<RefCell<Vec<Option<ComponentType>>>>()
        {
            return Some(component_vec.borrow_mut());
        }
    }
    None
}

遇到的问题

当我尝试获取Controller组件的可变引用,并调用它的update方法传入&mut scene时,触发了Rust的借用检查错误。相关代码如下:

let mut scene = self.scenes[0].write().unwrap();
let mut controllers: std::cell::RefMut<'_, Vec<Option<Box<dyn Controller>>>> = scene.borrow_component_vec_mut::<Box<dyn Controller>>().unwrap();
for (index, mut controller) in controllers.iter_mut().enumerate() {
    if let Some(controller) = controller.as_mut(){
        controller.update(index, &mut scene);
    }
}
drop(controllers);
drop(scene);

我理解问题所在:borrow_component_vec_mut方法需要接收&self(不可变引用),但此时我已经持有了scene的可变引用,Rust的借用规则不允许同一对象同时存在可变和不可变引用,哪怕我之后会立刻drop掉这些引用也不行。而且我不能用不可变方式借用Controller,因为它内部状态需要修改;尝试用Arc<RwLock>包装Scene又会在调用写锁时阻塞,也不是可行方案。

当前临时解决方案

在社区朋友的建议下,我把Controller包装成Arc<Mutex<Box<dyn Controller>>>,并且先把需要处理的Controller克隆出来,释放scene的借用后再执行更新逻辑,这样确实解决了问题:

let mut scene = self.scenes[0].write().unwrap();
let mut test = vec![];
let mut controllers = scene.borrow_component_vec_mut::<Arc<Mutex<Box<dyn Controller>>>>().unwrap();
for (index, controller) in controllers.iter().enumerate() {
    if let Some(controller) = controller {
        test.push((index, controller.clone()));
    }
}
drop(controllers);
drop(scene);
for (index, mut controller) in test {
    let mut controller = controller.lock().unwrap();
    controller.update(index, self.scenes[0].clone());
}

但这种方式感觉有点“重量级”,总觉得应该有更简洁、更符合ECS设计思路的方案,想请教各位有没有更好的解决办法?

备注:内容来源于stack exchange,提问作者C. Dow

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 09:59:32