我想在堆上有一个带有两个引用的结构;一个给我,另一个来自封闭。请注意,该代码适用于单线程情况:
use std::rc::Rc;
#[derive(Debug)]
struct Foo {
val: u32,
}
impl Foo {
fn set_val(&mut self, val: u32) {
self.val = val;
}
}
impl Drop for Foo {
fn drop(&mut self) {
println!("we drop {:?}", self);
}
}
fn need_callback(mut cb: Box<FnMut(u32)>) {
cb(17);
}
fn create() -> Rc<Foo> {
let rc = Rc::new(Foo { val: 5 });
let weak_rc = Rc::downgrade(&rc);
need_callback(Box::new(move |x| {
if let Some(mut rc) = weak_rc.upgrade() {
if let Some(foo) = Rc::get_mut(&mut rc) {
foo.set_val(x);
}
}
}));
rc
}
fn main() {
create();
}
在真实代码中,need_callback
会将回调保存到某个地方,但在此之前可能会cb
调用need_callback
。
代码显示std::rc::Rc
不适合此任务,因为永远不会调用foo.set_val(x)
;在这种情况下,我有两个强引用,Rc::get_mut
给出了None
。
我应该使用哪个带引用计数的智能指针而不是std::rc::Rc
来调用foo.set_val
?也许可以修复我的代码并仍使用std::rc::Rc
?
经过一番思考后,我需要像std::rc::Rc
这样的东西,但弱引用应该可以防止掉线。当我需要可变性时,我可以有两个弱引用并将它们升级为强大。
因为它是一个单线程程序,所以我一次只能有很强的参考,所以一切都会按预期工作。
答案 0 :(得分:3)
Rc
(及其多线程对手Arc
)只关注所有权。现在有联合所有权,而不是单一所有者,在运行时跟踪。
可变性是一个不同的概念,虽然与所有权密切相关:如果你拥有一个值,那么你就有能力改变它。这就是Rc::get_mut
仅在有一个强引用时才有效的原因 - 它与说单个所有者相同。
如果您需要能够以不与程序结构匹配的方式划分可变性,则可以使用Cell
或RefCell
等工具进行单线程程序:< / p>
use std::cell::RefCell;
fn create() -> Rc<RefCell<Foo>> {
let rc = Rc::new(RefCell::new(Foo { val: 5 }));
let weak_rc = Rc::downgrade(&rc);
need_callback(move |x| {
if let Some(rc) = weak_rc.upgrade() {
rc.borrow_mut().set_val(x);
}
});
rc
}
多线程上下文中的
use std::sync::Mutex;
fn create() -> Rc<Mutex<Foo>> {
let rc = Rc::new(Mutex::new(Foo { val: 5 }));
let weak_rc = Rc::downgrade(&rc);
need_callback(move |x| {
if let Some(rc) = weak_rc.upgrade() {
if let Ok(mut foo) = rc.try_lock() {
foo.set_val(x);
}
}
});
rc
}
这些工具都推迟检查只有一个可变引用运行时,而不是编译时。