【问题标题】:What is the right smart pointer to have multiple strong references and allow mutability?拥有多个强引用并允许可变性的正确智能指针是什么?
【发布时间】:2016-08-21 14:21:23
【问题描述】:

我想在堆上有一个带有两个引用的结构;一个给我,另一个来自关闭。注意代码是针对单线程情况的:

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 将回调保存到某个位置,但在此之前可能会像 need_callback 那样调用 cb

代码显示std::rc::Rc不适合这个任务,因为foo.set_val(x)从未被调用过;我有两个强引用,Rc::get_mut 在这种情况下给出了None

我应该使用什么具有引用计数的智能指针而不是std::rc::Rc 才能调用foo.set_val?也许可以修复我的代码并仍然使用std::rc::Rc

经过一番思考,我需要std::rc::Rc 之类的东西,但弱引用应该防止丢弃。我可以有两个弱引用,并在需要可变性时将它们升级为强引用。

因为它是一个单线程程序,我一次只有强引用,所以一切都会按预期工作。

【问题讨论】:

    标签: rust smart-pointers


    【解决方案1】:

    Rc(及其多线程对应物Arc)只关心所有权。现在不再是单一所有者,而是在运行时跟踪共同所有权。

    可变性是一个不同的概念,尽管与所有权密切相关:如果你拥有一个值,那么你就有能力改变它。这就是为什么Rc::get_mut 仅在有一个强引用时才有效——这与说只有一个所有者相同。

    如果您需要以与程序结构不匹配的方式划分可变性的能力,您可以将CellRefCell 等工具用于单线程程序:

    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
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多