Hi, I'm trying to understand the concepts of interior mutability, but I'm finding a hard time actually implementing it! I do not know if this is possible to achieve this without such pattern, but i would love your insights!
The problem is this:
I have a struct with a HashMap of vectors with trait objects called entities:
entities: HashMap<usize, Vec<Rc<dyn Component>>>
...and a field for a HashMap with mutable refrences to the component object who is owned by the entity vectors:
I do not know if Rc is the correct pattern to use in this case, and this is my question, How would i implement a function for adding subscriptions, and what interior mutability pattern should i use if any?
The refrence in the subscription field is mutable as i want to call a mut trait method on the component. How would i call this function?
This is my test case:
struct TestComponent(u32);
impl Component for TestComponent {
fn on(&mut self, /*...*/) {
println!("...");
}
}
fn main() {
let mut world = World::new(); // The struct this article is about
let test = TestComponent(4);
// ...
world.add_component(&entity, test);
world.add_subscription(UnrelatedEnum::Something, test); // i need help here!
}
Edit: I am also interested in a function to remove components from the entities field safely, although I have found this to be quite contradicting to the purpose of interior mutability.
Thanks for the quick and good response @quinedot! It really helped me understand the differences between Rc and RefCell, but i still get a wierd error while integrating your answer to my code.
I want to run the on function in a method emit(&mut self, event: &UnrelatedEnum), but i get a wierd result from the borrow_mut() function.
I have tried to follow the type as explicitly as i can to demonstrate the issue:
pub fn emit(&mut self, event: &UnrelatedEnum) -> anyhow::Result<()> {
if let Some(comps) = map {
let comps: &mut Vec<Rc<RefCell<dyn Component>>> = comps;
let component: RefMut<'static, (dyn Component + 'static> = comps.last().borrow_mut(); // This line does not compile with the mismatched types error below
component.on();
}
}
Error message:
expected RefMut<'_, (dyn Component + 'static)>,
found &mut &Rc<RefCell<dyn Component>>
This makes sense as Rc::borrow_mut() return &mut T, but i want to call the borrow_mut function of RefCell, as @quinedot does in the playground linked:
let vec: &mut Vec<Rc<RefCell<dyn Component>>> = self.subscriptions.get_mut(&ue).unwrap();
vec.push(Rc::new(RefCell::new(component)));
let last: &Rc<RefCell<(dyn Component + 'static)>> = vec.last().unwrap();
last.borrow_mut().on();
I have once again tried writing explicit types for everything to make it easier to debug, and this piece of code compiles. What is the problem with my code snippet?
You have hit an unfortunate name conflict with a different borrow_mut(). Your IDE has probably tried to help by importing the trait std::borrow::BorrowMut for you. You must remove that import (or add some explicit dereferencing to get the right borrow_mut() method selected, but that's trickier).
Pedantic completionism for niche cases that don’t apply here
get_mut (if the Arc is actually unique) and make_mut (for copy-on-write behavior) will also give you &mut pointing inside the Arc without requiring extra interior mutability helpers.