22

In the following code, I am trying to change the value of a refcounted object by calling one of its methods:

use std::rc::Rc;

fn main() {
    let mut x = Rc::new(Thing { num: 50 });
    x.what_to_do_to_get_mut_thing().change_num(19); //what do i do here
}

pub struct Thing {
    pub num: u32,
}

impl Thing {
    pub fn change_num(&mut self, newnum: u32) {
        self.num = newnum;
    }
}

I am using the get_mut function to achieve this, but I don't know if this is a standard way to accomplish this.

if let Some(val) = Rc::get_mut(&mut x) {
    val.change_num(19);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
ZNackasha
  • 755
  • 2
  • 9
  • 29

2 Answers2

35

The documentation for Rc says:

See the module-level documentation for more details.

Which has this text:

This is difficult because Rc enforces memory safety by only giving out shared references to the value it wraps, and these don't allow direct mutation. We need to wrap the part of the value we wish to mutate in a RefCell, which provides interior mutability: a method to achieve mutability through a shared reference. RefCell enforces Rust's borrowing rules at runtime.

It then demonstrates how to use it.


If you didn't read the API documentation, you might have instead chosen to read the entire chapter about Rc in The Rust Programming Language. It has this to say:

Via immutable references, Rc<T> allows you to share data between multiple parts of your program for reading only. If Rc<T> allowed you to have multiple mutable references too, you might violate one of the borrowing rules discussed in Chapter 4: multiple mutable borrows to the same place can cause data races and inconsistencies. But being able to mutate data is very useful! In the next section, we’ll discuss the interior mutability pattern and the RefCell<T> type that you can use in conjunction with an Rc<T> to work with this immutability restriction.


Applying this new knowledge to your code:

use std::{cell::RefCell, rc::Rc};

fn main() {
    let x = Rc::new(RefCell::new(Thing { num: 50 }));
    x.borrow_mut().change_num(19);
}

See also:

I am using the get_mut function

It's very unlikely that you want to use this.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
8

This isn't necessarily the OP's question, but a huge pitfall that can lead to issues with Rc<RefCell<X>> can be tracked down to the BorrowMut trait.

If you have run into the issue where calling .borrow_mut() on an Rc<RefCell<X>> returns another Rc<RefCell<X>>, make sure that the BorrowMut trait is not being used at the top of your script.

Rust-Analyzer automatically adds it in, and it completely changes the behavior of that function. I learned about this after many hours of debugging when I stumbled upon this issue.

Joseph Meadows
  • 160
  • 2
  • 8
  • 1
    This finally was the answer as to why I just couldn't get a mutable borrow when trying to mutate under a &Rc>. I just cept on calling .borrow_mut().borrow_mut()....Thank you for commenting this – djkato Jul 23 '23 at 19:11