use std::{cell::RefCell, rc::{Rc, Weak}};
struct Parent {
pub child: Option<Child>,
}
impl Parent {
pub fn get_value(&self) {
print!("30");
}
pub fn set_child(&mut self) {
let child = Child {
parent: Some(self),
};
self.child = Some(child);
}
pub fn run(&self) {
println!("run");
if let Some(c) = &self.child {
c.borrow().call_parent();
}
}
}
struct Child {
pub parent: Option<Parent>,
}
impl Child {
pub fn call_parent(&self) {
self.parent.unwrap().get_value();
}
}
fn main() {
println!("Hello, world!");
let mut p = Parent { child: None };
p.set_child();
p.run();
}
以上代码无法运行,尝试改了几次后,都陷入类似无解的困境,这种情况在其他语言中一般很好实现,但是感觉在Rust中困难重重困难,请高手给指点下。
1
共 12 条评论, 1 页
评论区
写评论看来你没领略作者的意思,人家并非只是想要包含的关系,而是,从p->c,或者从c->p都可以访问,也就是父节点可以直接访问到子节点,而子节点也可以直接访问回父节点,看懂没?
--
👇
北海: 这个是典型的面向对象中的模板方法,子类需要调用父类的方法才能完成功能的子类,C++/Java/C#可以轻易做到。
而Rust,可以看下这篇将Rust实现模板方法的文章
《Rust与面相对象二:模板方法》
如果你读懂了这篇文章,子级调用父级的方法,写法结构要变一下,应该是如下设计比较好
struct Parent { child: C, }中得C是泛型得意思?为什么不用T呢?很奇怪得命名方式
👇
北海: 这个是典型的面向对象中的模板方法,子类需要调用父类的方法才能完成功能的子类,C++/Java/C#可以轻易做到。
而Rust,可以看下这篇将Rust实现模板方法的文章
《Rust与面相对象二:模板方法》
如果你读懂了这篇文章,子级调用父级的方法,写法结构要变一下,应该是如下设计比较好
这个是典型的面向对象中的模板方法,子类需要调用父类的方法才能完成功能的子类,C++/Java/C#可以轻易做到。
而Rust,可以看下这篇将Rust实现模板方法的文章
《Rust与面相对象二:模板方法》
如果你读懂了这篇文章,子级调用父级的方法,写法结构要变一下,应该是如下设计比较好
如果是想要线程安全的版本,,那就不应该使用Rc或者RefCell了,而是应该使用Arc和Mutex来保护数据,下面是正确的代码:
楼下的才是正解。你这个明显复杂化了
--
👇
ZZG: 在不改变原来框架的情况下好像真的很难,因为
导致p是在本地的,任何想让child持有Rc都是不可能的,因为这个parent已经是在栈上了的,只能拿普通的&Parent引用(如果是可变引用&mut Parent就更难了,可能做不了),所以改成
因为Child现在有了一个lifetime参数'a,那么因为Parent结构体中有Child,所以Parent自己肯定也要有一个lifetime参数,所以Parent会长这样
这个SomeType是什么呢,能想到的可能是Box,Rc,Cell,RefCell这些智能指针,或者干脆什么都不要直接Option<Child<'a>>.
然后我们希望调用
set_child会生成某个Child实例,且将parent的None改成有内容的Some(child),把None改成Some一定需要&mut p,但我们又想构造出一个一直持有parent引用的Child实例,这里会有冲突(能不能解决我也不懂)。
为了避免&mut p和&p不能同时存在的问题,解决方法是上面的SomeType使用RefCell,而且在最开始初始化Parent的时候,不是设置为None,而是RefCell,这样可以用&p来将None改成Some
最后完整代码如下,Parent中新增了一个id主要看child是不是指向了我们原来的那个Parent.
最后这个实现是没有memory leak的,但是限制也比较大,就是p在set_child后不能再调用任何&mut 方法
学习了,但这个缺点也挺致命的,感觉Rust越学越感觉水有点深。
--
👇
ZZG: 在不改变原来框架的情况下好像真的很难,因为
导致p是在本地的,任何想让child持有Rc都是不可能的,因为这个parent已经是在栈上了的,只能拿普通的&Parent引用(如果是可变引用&mut Parent就更难了,可能做不了),所以改成
因为Child现在有了一个lifetime参数'a,那么因为Parent结构体中有Child,所以Parent自己肯定也要有一个lifetime参数,所以Parent会长这样
这个SomeType是什么呢,能想到的可能是Box,Rc,Cell,RefCell这些智能指针,或者干脆什么都不要直接Option<Child<'a>>.
然后我们希望调用
set_child会生成某个Child实例,且将parent的None改成有内容的Some(child),把None改成Some一定需要&mut p,但我们又想构造出一个一直持有parent引用的Child实例,这里会有冲突(能不能解决我也不懂)。
为了避免&mut p和&p不能同时存在的问题,解决方法是上面的SomeType使用RefCell,而且在最开始初始化Parent的时候,不是设置为None,而是RefCell,这样可以用&p来将None改成Some
最后完整代码如下,Parent中新增了一个id主要看child是不是指向了我们原来的那个Parent.
最后这个实现是没有memory leak的,但是限制也比较大,就是p在set_child后不能再调用任何&mut 方法
在不改变原来框架的情况下好像真的很难,因为
导致p是在本地的,任何想让child持有Rc都是不可能的,因为这个parent已经是在栈上了的,只能拿普通的&Parent引用(如果是可变引用&mut Parent就更难了,可能做不了),所以改成
因为Child现在有了一个lifetime参数'a,那么因为Parent结构体中有Child,所以Parent自己肯定也要有一个lifetime参数,所以Parent会长这样
这个SomeType是什么呢,能想到的可能是Box,Rc,Cell,RefCell这些智能指针,或者干脆什么都不要直接Option<Child<'a>>.
然后我们希望调用
set_child会生成某个Child实例,且将parent的None改成有内容的Some(child),把None改成Some一定需要&mut p,但我们又想构造出一个一直持有parent引用的Child实例,这里会有冲突(能不能解决我也不懂)。
为了避免&mut p和&p不能同时存在的问题,解决方法是上面的SomeType使用RefCell,而且在最开始初始化Parent的时候,不是设置为None,而是RefCell,这样可以用&p来将None改成Some
最后完整代码如下,Parent中新增了一个id主要看child是不是指向了我们原来的那个Parent.
最后这个实现是没有memory leak的,但是限制也比较大,就是p在set_child后不能再调用任何&mut 方法
@jian 给出的代码似乎还真不存在循环引用:
首先 main 开始时在栈上创建了一个
Parent
(p
), 内容为空指针。然后
p.set_child()
首先以自身(self)为源复制(self.clone()
)了一个Parent
到栈上,然后把它包在Rc
里又复制到了堆上;这之后又在栈上创建了一个Child
,让Child.parent
指向堆上的那个Parent
,然后也将这个Child
包在Rc
里复制到了堆上,最后让self.child
指向在堆上的那个Child
。也就是说,set_parent()
实际上创建了另一个新的(在堆上的)Parent
,然后创建了一个(在堆上的)指向这个新Parent
的Child
,最后设置self.child
指向这个Child
。如果再来一次
set_child()
,又会创建新的Parent3
(复制Parent1
得到) 和Child2
:我觉得楼主的这个问题的一个难点是,
Parent.set_child()
调用时需要一个对Parent
的“已被引用计数的引用”(Rc<Parent>
),这就要求Parent
先被纳入引用计数系统。P.S. 在我有限的使用
Weak
的经验里,似乎没有出现很多生命周期问题。如果我记得没错的话,用Rc::downgrade(&rc)
就可以获得一个Weak
;然后使用它的时候用Weak::upgrade(&weak)
,如果此时所指对象还在,就可以获得一个Rc
了。有一点不明白,这里不存在循环引用吗?我一开始想着用Weak来着,结果最后陷入到各种
'a
的报错循环中。--
👇
jian:
可以刷刷题来熟悉熟悉这种Option<Rc<RefCell>>的循环引用模式。
不过也就熟悉熟悉,感觉正儿八经的地方都用unsafe封装一段来用。
多谢,可以运行。
--
👇
jian: