使用内部可变性模式的“BorrowMutError”

Posted

技术标签:

【中文标题】使用内部可变性模式的“BorrowMutError”【英文标题】:"BorrowMutError" using the Interior Mutability Pattern 【发布时间】:2020-12-30 23:44:29 【问题描述】:

我正在尝试使用内部可变性模式来共享可变引用。

但是,当我尝试使用与它共享的结构中的引用时,程序会出现以下错误:

thread 'main' panicked at 'already borrowed: BorrowMutError'

代码如下:

use std::rc::Rc;
use std::cell::RefCell;

fn main() 
    let game = Game::init();
    game.start();


struct Game 
    ecs: Rc<RefCell<Ecs>>,


impl Game 
    pub fn init() -> Game 
        let ecs = Rc::new(RefCell::new(Ecs::new()));
        ecs.borrow_mut().register_systems(vec![
            Box::new(Renderer 
                ecs: Rc::clone(&ecs),
            ),
        ]);
        Game 
            ecs: Rc::clone(&ecs),
        
    

    pub fn start(&self) 
        self.ecs.borrow_mut().update();
    


struct Ecs 
    systems: Vec<Box<dyn System>>,


impl Ecs 
    fn new() -> Ecs 
        Ecs 
            systems: vec![],
        
    

    fn register_systems(&mut self, systems: Vec<Box<dyn System>>) 
        self.systems = systems;
    

    fn update(&self) 
        for system in self.systems.iter() 
            system.update();
        
    

    fn test(&self) 
        println!("done!");
    


trait System 
    fn update(&self);



struct Renderer 
    ecs: Rc<RefCell<Ecs>>,


impl System for Renderer 
    fn update(&self) 
        self.ecs.borrow_mut().test();
    

问题似乎出在这条线上:

self.ecs.borrow_mut().test();

这里有什么问题?跟性格有关系吗?还是我需要以其他方式调用函数test

【问题讨论】:

这条信息似乎完全正确:你 borrow_mut() 两次使用相同的 RefCell(通过两个不同的 Rc 指向它的指针),这正是你不能做的事情。 【参考方案1】:

确实,Renderer 中的 ecs 成员是 ecs 成员的克隆 在Game,即他们都拥有相同的Ecs

当你 borrow_mut() ecs 成员在 Game 然后迭代 元素,你到达Renderer 其中borrow_mut()s 同样Ecs。 这是在运行时检测到的,然后发生恐慌,这是预期的 RefCell 的行为。

如果您在这两种情况下都将borrow_mut() 更改为borrow(), 这不再恐慌,因为多个不可变借用 是允许的。

我不知道这段代码的确切用途,但我不确定 从Renderer 借用Ecs 是个好主意。 我认为内部可变性应该适用于每个 单独存储组件而不是整个Ecs

【讨论】:

以上是关于使用内部可变性模式的“BorrowMutError”的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程编程之不可变对象模式

java类增强方式

需要多个参数输入时-----------------考虑使用变种的Builder模式

设计模式一建造者模式

不变模式

实现具有内部可变性的索引