在这个leetcode反二叉树问题中,我试图借用一个包在一个Rc中的节点。
use std::rc::Rc;
use std::cell::RefCell;
impl Solution {
pub fn invert_tree(root: Option<Rc<RefCell<TreeNode>>>) -> Option<Rc<RefCell<TreeNode>>> {
let mut stack: Vec<Option<Rc<RefCell<TreeNode>>>> = vec![root.clone()];
while stack.len() > 0 {
if let Some(node) = stack.pop().unwrap() {
let n: &mut TreeNode = &mut node.borrow_mut();
std::mem::swap(&mut n.left, &mut n.right);
stack.extend(vec![n.left.clone(), n.right.clone()]);
}
}
root
}
}
如果我把let n: &mut TreeNode
改为let n = &mut node.borrow_mut()
,我会在下一行得到一个编译器错误,“cannot borrow *n
as mutable more once at at time”看起来编译器推断n是&mut RefMut<TreeNode>
类型,但是当我明确地说它是&mut TreeNode
时,一切都解决了。为什么呢?
1条答案
按热度按时间yquaqz181#
借位拆分和deref强制的组合会导致看似相同的代码行为不同。
编译器推断
n
的类型为RefMut<TreeNode>
,因为borrow_mut
实际返回的是RefMut<TreeNode>
:RefMut
是一个很有趣的小类型,设计成 * 看起来 * 像&mut
,但它实际上是一个独立的东西。它实现了Deref
和DerefMut
,所以在需要的时候它会很高兴地假装成&mut TreeNode
。但Rust仍然为您插入对.deref()
的调用。现在,为什么一个可以工作而另一个不行呢?
所以我们试图在同一行中对同一个变量两次调用
deref_mut
(它接受一个&mut self
),这是Rust的借位规则所不允许的,所以它失败了。(Note第一行中的
&mut
只是毫无理由地借用了一个拥有的值。临时生存期扩展让我们可以避开这个问题,尽管在这种情况下您根本不需要&mut
)现在,另一方面,如果你 do 放入类型注解,那么Rust看到
borrow_mut
返回RefMut<'_, TreeNode>
,而你要求的是&mut TreeNode
,所以它在 * 第一 * 行插入deref_mut
。现在,唯一的
deref_mut
调用出现在第一行,然后,在第二行,我们同时访问n.left
和n.right
,两者都是 * 可变的 *,看起来 * 我们同时访问n
两次,但Rust实际上足够聪明,能够看到我们同时访问n
的两个不相交的部分,Rust会在不同的示例字段上拆分借位,但它不够智能,无法看到deref_mut
调用之间的拆分(原则上,函数调用可以做任何事情,所以Rust的借位检查器拒绝尝试对它们的返回值进行高级推理)。