Cannot borrow data in 'Rc' as mutable

404 views Asked by At

I've been writing a ray tracer in Rust and I've come across a problem. I have defined the following struct:

pub struct HitRecord{
     mat : Rc<RefCell<dyn Material>,
     ...
}

The Material trait has only one associated function:

pub trait Material{
fn scatter(&mut self,r_in : &mut Ray, rec : &HitRecord, attenuation : &mut Color, scattered : &mut Ray) -> bool; 
}

That some structs (e.g. Lambertian and Metal) implement. I don't understand how I'm supposed to call this function from another piece of code:

    let mut rec : HitRecord = HitRecord::new(); 

    if world.hit(r, &mut Interval{min : 0.001, max : INFINITY} , &mut rec){
        let mut scattered = Ray::new();
        let mut attenuation = Color::new();
        let temp = rec.mat.clone();

        if temp.scatter(r, &rec, &mut attenuation,&mut scattered) {
            return attenuation * self.ray_color(&mut scattered,depth-1,world);
        }   

        return Color::new();
    }

The function call world.hit() requires rec to be mutable, since it assigns to it. The problem occurs in the second if statement:

error[E0596]: cannot borrow data in an `Rc` as mutable
--> camera.rs:128:16
|
|             if temp.get_mut().scatter(r, &rec, &mut attenuation,&mut scattered) {
|                ^^^^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<RefCell<dyn material::Material>>`

I tried implementing DerefMut for dyn Material but I got different errors regarding Deref not being satisfied and upon implementing that various other errors; I wasn't able to implement it properly. I've also tried switching Rc<RefCell> with other things, such as Rc, Box, Rc<Box> to name a few, with fruitless results. I've searched for questions with similar error messages but I can't see how they apply to my case, if they do at all.

EDIT: A minimal example of the problem is as follows:

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

struct Stone;
trait Material {
    fn scatter(&mut self, rec : &HitRecord){
        println!("scattering");
    }
}

impl Material for Stone {}

pub struct HitRecord{
    pub mat : Rc<RefCell<dyn Material>>
}

impl HitRecord{
    pub fn new() -> Self{
        Self{
            mat : Rc::new(RefCell::new(Stone)) as Rc<RefCell<dyn Material>>
       }
    }
}

fn main() {
    let mut f = HitRecord::new();
    // None of these compile
    f.borrow_mut().scatter(&f.mat);
    f.mat.borrow_mut().scatter(&f.mat);
    f.mat.scatter(&f.mat);
}
2

There are 2 answers

0
cafce25 On BEST ANSWER

The problem is you're importing BorrowMut which also provides a different borrow_mut method, but you want to call Rc::borrow_mut so remove use std::borrow::BorrowMut and write the following:

fn main() {
    let f = HitRecord::new();
    f.mat.borrow_mut().scatter(&f);
}

Note though, that you cannot access rec.mat from inside that specific scatter call in any meaningful way because it's already borrowed exclusively so any attempt to borrow it again will panic.


It is extremely rare that you need to import BorrowMut especially in combination with Rc and RefCell you can usually just use the corresponding syntax: a.borrow_mut()&mut a

1
Masklinn On

The problem is that your get_mut makes no sense: the purpose of get_mut is to trivially provide a mutable reference to the wrapped value when you have a mutable reference to the refcell:

pub fn get_mut(&mut self) -> &mut T

But you don't have that, Rc will only hand out shared references (it only implements Deref)1.

The normal way to get a mutable reference out of a RefCell is borrow_mut, which checks at runtime that there is no outstanding borrow (mutable or not) then returns one. You don't even need to clone the rec.mat, unless you need to store a new instance.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8801c8388486b54739bbc8ffe14bc1c8

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

struct Stone;
trait Material {
    fn scatter(&mut self) {
        println!("scattering");
    }
}
impl Material for Stone {}

fn main() {
    let f = Rc::new(RefCell::new(Stone)) as Rc<RefCell<dyn Material>>;
    f.borrow_mut().scatter();
}

1: except for Rc::get_mut but that's far from normal use, it'll only work if that's the sole outstanding reference; and make_mut which will clone the internal value but that's not usually the case (or desirable) if you're using Rc.