Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.7k views
in Technique[技术] by (71.8m points)

rust - Explain the behavior of *Rc::make_mut and why it differs compared to Mutex

I needed to pass a resource between several functions which use a closure as an argument. And within these the data was handled, but it looked for that the changes that were realized to a variable will be reflected in the rest.

The first thing I thought was to use Rc. I had previously used Arc to handle the data between different threads, but since these functions aren't running in different threads I chose Rc instead.

The most simplified code that I have, to show my doubts:

The use of RefCell was because maybe I had to see that this syntax will not work as I expected:

*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);
}

The code is simplified, the scenario where it is used is totally different. I note this to avoid comments like "you can lend a value to the function", because what interests me is to know why the cases exposed work in this way.

stdout:

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 }

About test()

I'm new to Rust so I don't know if this crazy syntax is the right way.

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

When running test() you can see that the previous syntax works, because it increases the value, but this increase does not affect the clones. I expected that with the use of *Rc::make_mut(& mut rc_pref_temp)... that the clones of a shared reference would reflect the same values.

  • If Rc has references to the same object, why do the changes to an object not apply to the rest of the clones? Why does this work this way? Am I doing something wrong?

Note: I use RefCell because in some tests I thought that maybe I had something to do.

About test2()

I've got it working as expected using Mutex with Rc, but I do not know if this is the correct way. I have some ideas of how Mutex and Arc works, but after using this syntax:

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

With the use of Mutex in test2(), I wonder if Mutex is not only responsible for changing the data in but also the one in charge of reflecting the changes in all the cloned references.

Do the shared references actually point to the same object? I want to think they do, but with the above code where the changes are not reflected without the use of Mutex, I have some doubts.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You need to read and understand the documentation for functions you use before you use them. Rc::make_mut says, emphasis mine:

Makes a mutable reference into the given Rc.

If there are other Rc or Weak pointers to the same value, then make_mut will invoke clone on the inner value to ensure unique ownership. This is also referred to as clone-on-write.

See also get_mut, which will fail rather than cloning.

You have multiple Rc pointers because you called rc_pref.clone(). Thus, when you call make_mut, the inner value will be cloned and the Rc pointers will now be disassociated from each other:

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
}

The version with the Mutex works because it's completely different. You aren't calling a function which clones the inner value anymore. Of course, it doesn't make sense to use a Mutex when you don't have threads. The single-threaded equivalent of a Mutex is... RefCell!


I honestly don't know how you found Rc::make_mut; I've never even heard of it before. The module documentation for cell doesn't mention it, nor does the module documentation for rc.

I'd highly encourage you to take a step back and re-read through the documentation. The second edition of The Rust Programming Language has a chapter on smart pointers, including Rc and RefCell. Read the module-level documentation for rc and cell as well.

Here's what your code should look like. Note the usage of 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
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...