意外的自动取消引用行为
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()
。
但是,a
是 Rc
而不是 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
方法可用,因此不会发生自动解引用,我们的代码调用了错误的方法。
.
中对自动取消引用的工作原理进行了最全面的解释
当尝试在 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()
。
但是,a
是 Rc
而不是 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
方法可用,因此不会发生自动解引用,我们的代码调用了错误的方法。