6

when tried impl a double linked list in rust, i found below unexpected error

if let Some(link) = self.tail.take() {
    let x = link.borrow_mut();
    link.borrow_mut().next = Some(node.clone());
} else { ... }

here link is inferred to be Rc<RefCell<Node<..>>> and compiler says:

Cannot borrow immutable local variable link as mutable.

After tried, I guess when use std::borrow::BorrowMut, the error occurs.

// compiles
fn test1() {
    let a = Rc::new(RefCell::new(1));
    let b = RefCell::new(1);
    b.borrow_mut();
    a.borrow_mut();
}

// doesn't compile
fn test2() {
    use std::borrow::BorrowMut; // inserted this import!

    let a = Rc::new(RefCell::new(1));
    let b = RefCell::new(1);
    b.borrow_mut();
    a.borrow_mut();
}

here test2() fails to be compiled. I wanna know why it works this way.

Aloso
  • 5,123
  • 4
  • 24
  • 41
Turmiht Lynn
  • 71
  • 1
  • 4
  • 1
    Minimal WORKING code example would be helpful. You can use [rust play](https://play.rust-lang.org/) to share it. – S.R May 30 '20 at 22:42

1 Answers1

8

What you want to call is the method RefCell::borrow_mut().

However, a is a Rc and not a RefCell, so it has no borrow_mut method. This is where auto-dereferencing enters the picture. Because Rc<RefCell<T>> implements the Deref trait, it can be automatically dereferenced to a &RefCell<T> at method calls, and that's exactly what happens in the first test.

Now, if we import the BorrowMut trait, the test stops working. This is because the BorrowMut trait also has a method called borrow_mut, which is implemented for all types:

impl<T: ?Sized> BorrowMut<T> for T { ... }

This means that there is now a borrow_mut method available for Rc, so no auto-dereferencing occurs, and our code calls the wrong method.

The most comprehensive explanation of how auto-dereferencing works is in this SO answer.

Aloso
  • 5,123
  • 4
  • 24
  • 41