我正在尝试将一些面向对象的代码转换成Rust。在我遇到这种情况之前,一切都很顺利。
struct A {
root: Rc<RefCell<B>>,
}
struct B {
parent: Weak<RefCell<B>>,
c_lst: Vec<C>
value: u32
}
struct C {
parent: Weak<RefCell<B>>,
b_lst: Vec<Rc<RefCell<B>>>,
end: bool
}
我有三个结构体。我想让一个主结构体(这里是A
)保存结构体B
的根。B
和C
需要以只读方式访问父值,但在第一个Rc
出现的地方,它们需要是可变的。
开始时,调用此函数:
impl A {
fn register(&mut self) {
for c in self.root.borrow_mut().c_lst {
c.parent = Rc::downgrade(&self.root);
}
}
}
然后我在A
上使用一个update函数:
impl A {
fn update(&mut self) {
self.root.borrow_mut().update();
}
}
impl B {
fn update(&mut self) {
for c in &mut self.c_lst {
c.update();
}
}
}
impl C {
fn update(&mut self) {
match self.parent.upgrade() {
Some(parent) => {
// Fail because parent is already borrowed to call this update
if parent.try_borrow().unwrap().value == 0 {
// Do stuff
}
},
None => Panic!("Parent was freed")
}
if some_condition {
self.spawn_b();
}
for b in &self.b_lst {
b.borrow_mut().update();
}
}
// This infinite loop in this state, but it's for the example
fn spawn_b(&mut self) {
let b = Rc::new(RefCell::new(B::new()));
b.borrow_mut().parent = self.parent.clone();
if !self.end {
b.borrow_mut().c_lst = vec![C::new(Rc::downgrade(&b)), C::new(Rc::downgrade(&b))];
for c in b.borrow_mut().c {
c.spawn_b();
}
}
self.b_lst.push(b);
}
}
正如您在代码中看到的,我无法访问我的父状态。有什么建议吗?
3条答案
按热度按时间klr1opcd1#
示例中的Rc/Weak树是在Rust中实现带有父引用的树的一种简单方法。唯一的问题是,
update
函数需要对更新节点的可变访问,这会锁定较低的节点访问父节点。您将不得不进行架构更改来解决此问题。以下是几种方法:
RefCell
甚至Cell
。在您自己的节点更新时锁定主数据,然后在递归遍历子节点之前解锁主数据,让它们可以访问。ckocjqey2#
尽量避免在Rust中编写“面向对象”的代码。更喜欢组合而不是继承。如果你使用
Rc
来表示一个树形结构,那么要非常小心你指向“父”的方式。如果父也是Rc
,那么你将有内存泄漏,因为那会产生一个循环。要打破这些,你可以使用Weak
参考。也许阅读这本书的section会对你有帮助。ef1yzkbh3#
最后我使用了原始ptr。根据定义,这个解决方案使用的是
unsafe
。重要的是要注意,当一个新的
B
被推到C.b_lst
,他的内存地址不必改变,如果你只推,它会工作,但使用remove()
,swap_remove()
将改变B
的位置和他们的内存地址,儿童家长将指向一个坏B,或恐慌。要解决这个问题,要么在Vec上创建您的实现,以将
B
保持在创建的位置,要么当B
地址发生变化时,在所有子级上进行迭代,以将新地址重新分配给父级。我检查这个作为答案,但如果有人找到一个更好的解决方案与钢筋混凝土我会改变。
编辑:当创建
Vec<B>
时,使用固定大小的Vec::with_capacity()
,或者其他crate。重要的是向量不会被重新分配。或者再次迭代所有子节点以应用新的父地址。