rustborrow-checkerdereferenceraytracing

Cannot borrow data in 'Rc' as mutable


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

Solution

  • 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