Split borrow of struct in a RefCell

Hello. I found something a little suboptimal when trying to split a borrow in a struct referenced by a a RefMut (from a RefCell). The borrow checker seems a little bit overeager.

Here's a simplified version of the code.

use std::cell::{RefCell};

pub struct SubStructA {
a : usize
}

pub struct SubStructB {
b : usize
}

impl SubStructB {

pub fn do_something_with_a(&mut self, a : &SubStructA) {
    println!("{}, {}", a.a, self.b);
}

}

pub struct TopStruct {
a : SubStructA,
b : SubStructB
}

fn main() {

//--SPLIT BORROW WORKS GREAT----------------------------------------------
let mut top_struct = TopStruct{a : SubStructA{a : 1}, b : SubStructB{b : 2}};

let my_top_ref = &mut top_struct;

my_top_ref.b.do_something_with_a(&my_top_ref.a);

//--FAILS TO COMPILE----------------------------------------------
let top_ref_cell : RefCell<TopStruct> = RefCell::new(TopStruct{a : SubStructA{a : 1}, b : SubStructB{b : 2}});

let mut my_top_ref = top_ref_cell.borrow_mut();

my_top_ref.b.do_something_with_a(&my_top_ref.a);

}

And the borrow checker says this:

error[E0502]: cannot borrow `my_top_ref` as immutable because it is also borrowed as mutable
--> src/main.rs:34:39
|
34 | my_top_ref.b.do_something_with_a(&my_top_ref.a);
|    ----------    ------------------- ^^^^^^^^^^ immutable borrow occurs here
|    |             |
|    |             mutable borrow later used by call
|    mutable borrow occurs here

Anyway, there are several work-arounds I've found so it's not critical. It just struck me as a place where things weren't orthogonal, and I was wondering if anyone had any opinions about it. ie. whether I'm thinking about refs all wrong, or whether it's a rough edge in the language that could be smoothed out?

Thanks everyone.

This is because my_top_ref is a RefMut<TopStruct>, so each access to a field goes through the Deref trait. If you go through the deref trait once, and then split the borrow, it should work.

let mut my_top_ref = top_ref_cell.borrow_mut();
let mut my_top_ref = &mut *my_top_ref;

my_top_ref.b.do_something_with_a(&my_top_ref.a);
1 Like

That's exactly the work around that I found. I just didn't know what was happening behind the scenes.

Thanks for the explanation!