#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
.