在这个leetcode倒置二叉树问题中,我试图borrow 一个可变地包装在RC中的 node .以下是代码.

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 Born *n as Mariable At More One Time" 似乎编译器推断n是&mut RefMut<TreeNode>类型,但当我显式地说它是&mut TreeNode时,一切都解决了.有什么原因吗?

推荐答案

借入拆分和go 反强制的组合会导致看似相同的代码表现不同.

编译器推断nRefMut<TreeNode>类型,因为它是what borrow_mut actually returns:

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

RefMut是一个有趣的小类型,就像&mut一样被设计成look,但它实际上是另一回事.它实现了DerefDerefMut,所以当需要时,它会很高兴地假装成&mut TreeNode.但拉斯特仍在为你插入.deref()的呼叫.

现在,为什么一个能起作用,而另一个不行?在没有类型注释的情况下,插入deref后,您将获得

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

因此,我们try 在同一变量的同一行中调用deref_mut(取&mut self)两次.这是铁 rust 的借款规则所不允许的,因此它失败了.

(请注意,第一行上的&mut只是无缘无故地borrow 了拥有的值.临时生命周期延长可以让我们逃脱惩罚,即使在这种情况下您根本不需要&mut)

另一方面,如果您将do放入类型注释中,那么Rust看到borrow_mut返回RefMut<'_, TreeNode>,而您请求&mut TreeNode,所以它将deref_mut插入到first行.你得到

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

现在唯一的deref_mut电话是在一线上.然后,在第二行上,我们同时访问n.leftn.right,两者都是mutably.它looks就像我们同时两次可变地访问n,但Rust实际上足够聪明,可以看到我们同时访问n的两个互不相交的部分,所以它允许这样做.这就是所谓的borrow splitting.Rust将在不同的实例字段上拆分borrow ,但它不够智能,无法看到deref_mut调用之间的拆分(原则上,函数调用可以做任何事情,所以Rust的borrow 判断器拒绝try 对它们的返回值进行高级推理).

Rust相关问答推荐

为什么BitVec缺少Serialize trait?

如何为rust trait边界指定多种可能性

为什么铁 rust S似乎有内在的易变性?

在本例中,为什么我不能一次多次borrow 可变变量?

Rust 的多态现象.AsRef与Derf

程序在频道RX上挂起

在运行特定测试时,如何 suppress cargo test 的空输出?

在什么情况下 `..._or()` 比 `..._or_else(|| {})` 更好,为什么?

如何重命名 clap_derive 中的子命令占位符?

std mpsc 发送者通道在闭包中使用时关闭

将引用移动到线程中

在Rust中实现Trie数据 struct 的更好方式

go 重并堆积MPSC通道消息

Rust中是否可以在不复制的情况下从另一个不可变向量创建不可变向量?

在发布中包含 Rust DLL

使用 Rust 从 Raspberry Pi Pico 上的 SPI 读取值

返回迭代器的特征

n 个范围的笛卡尔积

编写 TOML 文件以反序列化为 struct 中的枚举

有什么办法可以用 Rust 访问 Windows 最近的文件夹吗?