Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access value in RefCell properly

I'm trying to wrap my head around Rc and RefCell in Rust. What I'm trying to achieve is to to have multiple mutable references to the same objects.

I came up with this dummy code:

use std::rc::Rc;
use std::cell::RefCell;

struct Person {
    name: String,
    mother: Option<Rc<RefCell<Person>>>,
    father: Option<Rc<RefCell<Person>>>,
    partner: Option<Rc<RefCell<Person>>>
}

pub fn main () {

    let mut susan = Person {
        name: "Susan".to_string(),
        mother: None,
        father: None,
        partner: None
    };

    let mut boxed_susan = Rc::new(RefCell::new(susan));

    let mut john = Person {
        name: "John".to_string(),
        mother: None,
        father: None,
        partner: Some(boxed_susan.clone())
    };

    let mut boxed_john = Rc::new(RefCell::new(john));

    let mut fred = Person {
        name: "Fred".to_string(),
        mother: Some(boxed_susan.clone()),
        father: Some(boxed_john.clone()),
        partner: None
    };

    fred.mother.unwrap().borrow_mut().name = "Susana".to_string();

    println!("{}", boxed_susan.borrow().name);

    // boxed_john.borrow().partner.unwrap().borrow_mut().name = "Susanna".to_string();
    // println!("{}", boxed_susan.borrow().name);

}

The most interesting part is this:

    fred.mother.unwrap().borrow_mut().name = "Susana".to_string();
    println!("{}", boxed_susan.borrow().name)

I change the name of Freds mother and then print out the name of Susan which should happen to be exactly the same reference. And surprise, surprise it prints out "Susana" so I am assuming that my little experiment of having shared mutable references was successful.

However, now I wanted to mutate it again this time accessing it as the partner of John which should also happen to be exactly the same instance.

Unfortunately when I comment in the following two lines:

// boxed_john.borrow().partner.unwrap().borrow_mut().name = "Susanna".to_string();
// println!("{}", boxed_susan.borrow().name);

I'm running into my old friend cannot move out of dereference of&-pointer. What am I doing wrong here?

like image 241
Christoph Avatar asked Aug 13 '14 22:08

Christoph


People also ask

How does RefCell work?

The RefCell<T> keeps track of how many Ref<T> and RefMut<T> smart pointers are currently active. Every time we call borrow , the RefCell<T> increases its count of how many immutable borrows are active. When a Ref<T> value goes out of scope, the count of immutable borrows goes down by one.

What is Interior mutability?

Interior mutability is a design pattern in Rust that allows you to mutate data even when there are immutable references to that data: normally, this action is disallowed by the borrowing rules.


1 Answers

This will fix it:

boxed_john.borrow().partner.as_ref().unwrap().borrow_mut().name = "Susanna".to_string();

The problem is the unwrap() on the Option<Rc<RefCell>>, which consumes the Option (ie moves out of it), but you only have a borrowed pointer. The as_ref converts the Option(T) to Option(&T) and unwrap converts it to &T, avoiding any move.

Also note: your variables have a lot more mutability than they really need. But I'm sure you're already seeing the compiler warnings for that.

like image 171
Raph Levien Avatar answered Oct 15 '22 05:10

Raph Levien