rust 为什么这里需要类型注解?

mrwjdhj3  于 2022-11-30  发布在  其他
关注(0)|答案(1)|浏览(166)

在这个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时,一切都解决了。为什么呢?

yquaqz18

yquaqz181#

借位拆分和deref强制的组合会导致看似相同的代码行为不同。
编译器推断n的类型为RefMut<TreeNode>,因为borrow_mut实际返回的是RefMut<TreeNode>

pub fn borrow_mut(&self) -> RefMut<'_, T>

RefMut是一个很有趣的小类型,设计成 * 看起来 * 像&mut,但它实际上是一个独立的东西。它实现了DerefDerefMut,所以在需要的时候它会很高兴地假装成&mut TreeNode。但Rust仍然为您插入对.deref()的调用。
现在,为什么一个可以工作而另一个不行呢?

let n = &mut node.borrow_mut();
std::mem::swap(&mut n.deref_mut().left, &mut n.deref_mut().right);

所以我们试图在同一行中对同一个变量两次调用deref_mut(它接受一个&mut self),这是Rust的借位规则所不允许的,所以它失败了。
(Note第一行中的&mut只是毫无理由地借用了一个拥有的值。临时生存期扩展让我们可以避开这个问题,尽管在这种情况下您根本不需要&mut
现在,另一方面,如果你 do 放入类型注解,那么Rust看到borrow_mut返回RefMut<'_, TreeNode>,而你要求的是&mut TreeNode,所以它在 * 第一 * 行插入deref_mut

let n: &mut TreeNode = &mut node.borrow_mut().deref_mut();
std::mem::swap(&mut n.left, &mut n.right);

现在,唯一的deref_mut调用出现在第一行,然后,在第二行,我们同时访问n.leftn.right,两者都是 * 可变的 *,看起来 * 我们同时访问n两次,但Rust实际上足够聪明,能够看到我们同时访问n的两个不相交的部分,Rust会在不同的示例字段上拆分借位,但它不够智能,无法看到deref_mut调用之间的拆分(原则上,函数调用可以做任何事情,所以Rust的借位检查器拒绝尝试对它们的返回值进行高级推理)。

相关问题