Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected auto deref behavior

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.

like image 267
Turmiht Lynn Avatar asked May 30 '20 20:05

Turmiht Lynn


1 Answers

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.

like image 127
Aloso Avatar answered Oct 11 '22 10:10

Aloso