【问题标题】:Explain the behavior of *Rc::make_mut and why it differs compared to Mutex解释 *Rc::make_mut 的行为以及它与 Mutex 相比的不同之处
【发布时间】:2017-05-16 22:24:03
【问题描述】:

我需要在几个使用闭包作为参数的函数之间传递资源。在这些数据中处理了数据,但它会寻找对变量实现的更改将反映在其余部分中。

我首先想到的是使用Rc。我之前使用Arc 来处理不同线程之间的数据,但由于这些函数不在不同的线程中运行,所以我选择了Rc

我拥有的最简化的代码,以表达我的疑问:

RefCell 的使用是因为也许我必须看到这个语法不会像我预期的那样工作:

*Rc::make_mut(&mut rc_pref_temp)...

use std::sync::Arc;
use std::rc::Rc;
use std::sync::Mutex;
use std::cell::RefCell;
use std::cell::Cell;

fn main() {
    test2();
    println!("---");
    test();
}

#[derive(Debug, Clone)]
struct Prefe {
    name_test: RefCell<u64>,
}

impl Prefe {
    fn new() -> Prefe {
        Prefe {
            name_test: RefCell::new(3 as u64),
        }
    }
}

fn test2(){
    let mut prefe: Prefe = Prefe::new();

    let mut rc_pref = Rc::new(Mutex::new(prefe));

    println!("rc_pref Mutex: {:?}", rc_pref.lock().unwrap().name_test);

    let mut rc_pref_temp = rc_pref.clone();

    *rc_pref_temp.lock().unwrap().name_test.get_mut() += 1;

    println!("rc_pref_clone Mutex: {:?}", rc_pref_temp.lock().unwrap().name_test);

    *rc_pref_temp.lock().unwrap().name_test.get_mut() += 1;

    println!("rc_pref_clone Mutex: {:?}", rc_pref_temp.lock().unwrap().name_test);

    println!("rc_pref Mutex: {:?}", rc_pref.lock().unwrap().name_test);
}

fn test(){
    let mut prefe: Prefe = Prefe::new();

    let mut rc_pref = Rc::new(prefe);

    println!("rc_pref: {:?}", rc_pref.name_test);

    let mut rc_pref_temp = rc_pref.clone();

    *((*Rc::make_mut(&mut rc_pref_temp)).name_test).get_mut() += 1;

    println!("rc_pref_clone: {:?}", rc_pref_temp.name_test);

    *((*Rc::make_mut(&mut rc_pref_temp)).name_test).get_mut() += 1;

    println!("rc_pref_clone: {:?}", rc_pref_temp.name_test);

    println!("rc_pref: {:?}", rc_pref.name_test);
}

代码简化,使用场景完全不同。我注意到这一点是为了避免诸如“您可以为函数提供价值”之类的 cmets,因为我感兴趣的是知道为什么暴露的案例以这种方式工作。

标准输出:

rc_pref       Mutex  : RefCell { value: 3 }
rc_pref_clone Mutex  : RefCell { value: 4 }
rc_pref_clone Mutex  : RefCell { value: 5 }
rc_pref       Mutex  : RefCell { value: 5 }
---
rc_pref              : RefCell { value: 3 }
rc_pref_clone        : RefCell { value: 4 }
rc_pref_clone        : RefCell { value: 5 }
rc_pref              : RefCell { value: 3 }

关于test()

我是 Rust 新手,所以我不知道这种疯狂的语法是否正确。

*((*Rc::make_mut(&mut rc_pref_temp)).name_test).get_mut() += 1;

运行test() 时,您可以看到前面的语法有效,因为它增加了值,但这种增加不会影响克隆。我希望通过使用*Rc::make_mut(&amp; mut rc_pref_temp)...,共享引用的克隆将反映相同的值。

  • 如果Rc 引用了同一个对象,为什么对一个对象所做的更改不适用于其余的克隆?为什么会这样?我做错了什么吗?

注意:我使用RefCell 是因为在某些测试中我认为也许我有事要做。

关于test2()

我已经使用MutexRc 让它按预期工作,但我不知道这是否是正确的方法。我对MutexArc 的工作原理有一些想法,但是在使用此语法之后:

*Rc::make_mut(&mut rc_pref_temp)...

test2()中使用Mutex,我想知道Mutex是否不仅负责更改数据,还负责反映所有克隆引用的更改。

共享引用实际上指向同一个对象吗?我想他们会这样做,但是对于上面的代码,如果不使用Mutex,更改不会反映出来,我有一些疑问。

【问题讨论】:

    标签: rust


    【解决方案1】:

    在使用之前,您需要阅读并理解所使用函数的文档。 Rc::make_mut says,强调我的:

    对给定的Rc 进行可变引用。

    如果有其他RcWeak指针指向相同的值,那么 make_mut 将在内部值上调用 clone 以确保唯一 所有权。这也称为写时克隆。

    另见get_mut,它将失败而不是克隆。

    您有多个Rc 指针因为您调用了rc_pref.clone()。因此,当您调用make_mut 时,内部值将被克隆,Rc 指针现在将彼此解除关联:

    use std::rc::Rc;
    
    fn main() {
        let counter = Rc::new(100);
        let mut counter_clone = counter.clone();
        println!("{}", Rc::strong_count(&counter));       // 2
        println!("{}", Rc::strong_count(&counter_clone)); // 2
    
        *Rc::make_mut(&mut counter_clone) += 50;
        println!("{}", Rc::strong_count(&counter));       // 1
        println!("{}", Rc::strong_count(&counter_clone)); // 1
    
        println!("{}", counter);       // 100
        println!("{}", counter_clone); // 150
    }
    

    带有Mutex 的版本可以工作,因为它完全不同。您不再调用克隆内部值的函数。当然,当你没有线程时使用Mutex 是没有意义的。 Mutex 的单线程等效项是... RefCell!


    我真的不知道你是怎么找到Rc::make_mut的;我以前从未听说过module documentation for cell 没有提及它,module documentation for rc 也没有提及。

    我强烈建议您退后一步,重新阅读文档。 second edition of The Rust Programming Language 有一个chapter on smart pointers,包括RcRefCell。阅读 rccell 的模块级文档。

    您的代码应该是这样的。注意borrow_mut的用法。

    fn main() {
        let prefe = Rc::new(Prefe::new());    
        println!("prefe: {:?}", prefe.name_test);             // 3
    
        let prefe_clone = prefe.clone();
        *prefe_clone.name_test.borrow_mut() += 1;
        println!("prefe_clone: {:?}", prefe_clone.name_test); // 4
    
        *prefe_clone.name_test.borrow_mut() += 1;
        println!("prefe_clone: {:?}", prefe_clone.name_test); // 5
        println!("prefe: {:?}", prefe.name_test);             // 5
    }
    

    【讨论】:

    • "我真的不知道你是怎么找到 Rc::make_mut 的;" - 有些人实际上阅读了struct docs。如果您单击右上角的[−],它们会很好地概述可用的方法。那或 IDE 自动完成。
    • @the8472 你的意思是我在第一句话中链接的结构文档并说“你真的需要在使用它之前阅读这个方法的作用”? ^_^ 我担心的主要是那里有一些被误导的资源,使用make_mut 说“这就是你的做法”,人们可能会在不理解的情况下跟随。
    • 我刚刚在this article 中了解了make_mut(),我猜它们展示了一个很好的用例:在级联样式表中,您可能有一个样式相同的孩子和父母,但是如果子样式不同,您可以使用make_mut() 创建它的克隆,然后您可以独立于父样式更新子样式。
    • @antoyo 哈哈,是的,我读到了,也想过这个问题?。我同意我们在那里看到的用例是有道理的,甚至可能是该方法首先存在的原因。我现在担心人们会看到这篇文章,甚至更想在不完全理解该函数的作用的情况下使用它。我想我们会看到的!
    猜你喜欢
    • 2011-04-01
    • 1970-01-01
    • 2013-11-05
    • 2014-03-29
    • 1970-01-01
    • 1970-01-01
    • 2012-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多