«BorrowMutError» с использованием шаблона внутренней изменчивости

#rust #borrow-checker #borrowing

#Ржавчина #проверка заимствования #заимствование

Вопрос:

Я пытаюсь использовать шаблон внутренней изменчивости для совместного использования изменяемой ссылки.

Однако, когда я пытаюсь использовать ссылку из структур, с которыми она совместно используется, программа паникует из-за ошибки:

 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(amp;ecs),
            }),
        ]);
        Game {
            ecs: Rc::clone(amp;ecs),
        }
    }

    pub fn start(amp;self) {
        self.ecs.borrow_mut().update();
    }
}

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

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

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

    fn update(amp;self) {
        for system in self.systems.iter() {
            system.update();
        }
    }

    fn test(amp;self) {
        println!("done!");
    }
}

trait System {
    fn update(amp;self);
}


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

impl System for Renderer {
    fn update(amp;self) {
        self.ecs.borrow_mut().test();
    }
}
  

Проблема, похоже, в строке:

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

В чем здесь проблема? Связано ли это с признаком? Или мне нужно вызвать функцию test другим способом?

Комментарии:

1. Сообщение кажется совершенно правильным: вы borrow_mut() вводите одно и то же RefCell дважды (через два разных Rc указателя на него), что именно то, что вы не должны делать.

Ответ №1:

Действительно, ecs элемент в Renderer является клоном ecs элемента в Game , т. Е. они оба владеют одним и тем же Ecs .

Когда вы borrow_mut() вводите ecs элемент в Game , затем выполняете итерацию по элементам, вы достигаете того, Renderer который borrow_mut() совпадает Ecs . Это обнаруживается во время выполнения, а затем вызывает панику, что соответствует предполагаемому поведению RefCell .

Если вы просто измените borrow_mut() на borrow() в обоих случаях, это больше не вызывает паники, поскольку разрешено несколько неизменяемых заимствований.

Я не знаю точно цели этого кода, но я не уверен, что заимствование Ecs всего из Renderer является хорошей идеей. Я бы предположил, что внутренняя изменчивость должна применяться к каждому сохраненному компоненту в отдельности, а не ко всему Ecs .