My main concern here is using Rc<RefCell<_>> as self, but the compiler disallows that.
But rather than that, I'm concerned if I'm doing something reasonable.
The real-world task is more complicated, but I need a container for items, and items have to know which container they are in. Also, I decided container should not own these items, cause they are already owned by another structure in the code.
Given that I need several references for my objects and they all have to be modifiable I decided I have to use Rc<RefCell<_>>
.
Here is the example code, that tries to model this.
I really do not like FooContainer::add(&container...
instead of container.add(...
.
But also, I feel this code is too complicated for this type of task.
use std::{
cell::RefCell,
rc::{Rc, Weak},
};
#[derive(Debug)]
struct Foo {
container_im_in: Option<Weak<RefCell<FooContainer>>>,
}
impl Foo {
fn new() -> Rc<RefCell<Foo>> {
Rc::new(RefCell::new(Foo {
container_im_in: None,
}))
}
}
#[derive(Debug)]
struct FooContainer {
vec: Vec<Weak<RefCell<Foo>>>,
}
impl FooContainer {
fn new() -> Rc<RefCell<FooContainer>> {
Rc::new(RefCell::new(FooContainer { vec: Vec::new() }))
}
fn add(self_: &Rc<RefCell<Self>>, foo: &Rc<RefCell<Foo>>) {
assert!(foo.borrow().container_im_in.is_none());
self_.borrow_mut().vec.push(Rc::downgrade(foo));
foo.borrow_mut().container_im_in = Some(Rc::downgrade(self_));
}
fn remove(self_: &Rc<RefCell<Self>>, foo: &Rc<RefCell<Foo>>) {
let prev_container = foo.borrow_mut().container_im_in.take().unwrap();
assert!(Weak::ptr_eq(&Rc::downgrade(self_), &prev_container));
let vec = &mut self_.borrow_mut().vec;
let foo_weak = Rc::downgrade(foo);
let pos = vec.iter().position(|it| Weak::ptr_eq(it, &foo_weak)).unwrap();
vec.remove(pos);
}
}
fn main() {
let foo1 = Foo::new();
let foo2 = Foo::new();
let container = FooContainer::new();
FooContainer::add(&container, &foo1);
dbg!(&container);
FooContainer::add(&container, &foo2);
dbg!(&container);
FooContainer::remove(&container, &foo1);
dbg!(&container);
FooContainer::remove(&container, &foo2);
dbg!(&container);
}