I am trying to use the Interior Mutability Pattern to share mutable reference.
However, when I try to use the reference from the structs it is shared with, the program panics with the error:
thread 'main' panicked at 'already borrowed: BorrowMutError'
Here is the code:
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();
}
}
The issue seems to be at the line:
self.ecs.borrow_mut().test();
What's the problem here? Is it related to the trait? Or do I need to call the function test in anoher way?
Indeed, the ecs member in Renderer is a clone of the ecs member
in Game, i.e. they both own the same Ecs.
When you borrow_mut() the ecs member in Game then iterate on
the elements, you reach the Renderer which borrow_mut()s the
same Ecs.
This is detected at runtime and then panics, which the intended
behaviour of RefCell.
If you just change borrow_mut() to borrow() in both cases,
this does not panic any more since multiple immutable borrows
are allowed.
I don't know exactly the purpose of this code but I'm not certain
that borrowing the Ecs as whole from Renderer is a good idea.
I would presume that the interior-mutability should apply to each
stored component individually rather than to the whole Ecs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With