意外的自动取消引用行为

Unexpected auto deref behavior

当尝试在 Rust 中实现双重 linked 列表时,我发现了以下意外错误

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

这里 link 被推断为 Rc<RefCell<Node<..>>> 并且编译器说:

Cannot borrow immutable local variable link as mutable.

试了一下,我猜是在use std::borrow::BorrowMut的时候,报错了

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

此处test2()编译失败。我想知道为什么会这样。

想要调用的是方法RefCell::borrow_mut()

但是,aRc 而不是 RefCell,因此它没有 borrow_mut 方法。这是自动取消引用进入画面的地方。因为 Rc<RefCell<T>> 实现了 Deref 特性,它可以在方法调用时自动取消引用到 &RefCell<T>,这正是第一个测试中发生的情况。

现在,如果我们导入 BorrowMut 特征,测试将停止工作。这是因为 BorrowMut trait 还有一个方法叫做 borrow_mut,也就是 implemented for all types:

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

这意味着 Rc 现在有一个 borrow_mut 方法可用,因此不会发生自动解引用,我们的代码调用了错误的方法。

.

中对自动取消引用的工作原理进行了最全面的解释