什么是具有多个强引用并允许可变性的正确智能指针?

时间:2016-08-21 14:21:24

标签: rust smart-pointers

我想在堆上有一个带有两个引用的结构;一个给我,另一个来自封闭。请注意,该代码适用于单线程情况:

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这样的东西,但弱引用应该可以防止掉线。当我需要可变性时,我可以有两个弱引用并将它们升级为强大。

因为它是一个单线程程序,所以我一次只能有很强的参考,所以一切都会按预期工作。

1 个答案:

答案 0 :(得分:3)

Rc(及其多线程对手Arc)只关注所有权。现在有联合所有权,而不是单一所有者,在运行时跟踪。

可变性是一个不同的概念,虽然与所有权密切相关:如果你拥有一个值,那么你就有能力改变它。这就是Rc::get_mut仅在有一个强引用时才有效的原因 - 它与说单个所有者相同。

如果您需要能够以不与程序结构匹配的方式划分可变性,则可以使用CellRefCell等工具进行单线程程序:< / 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
}
多线程上下文中的

MutexRwLockan atomic type

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
}

这些工具都推迟检查只有一个可变引用运行时,而不是编译时。