首页 > 解决方案 > 无法从匹配臂返回对成员的可变引用

问题描述

作为 rust 借用和生命周期的练习,我想实现一个简单的二叉树。但是,我被困在了一些事情上。考虑一下:

struct Node {
    key: i32,
    value: i32,
    left: Option<Box<Node>>,
    right: Option<Box<Node>>,
}

struct BinaryTree {
     root: Option<Box<Node>>,
}

impl BinaryTree {

    fn find_mut(&mut self, key: i32) -> &mut Option<Box<Node>> {
        let mut node = &mut self.root;
        loop {
            match node {
                Some(box_node) if box_node.key != key => {
                    node = if box_node.key < key {
                        &mut box_node.right
                    } else {
                        &mut box_node.left
                    }
                },
                other => return other
            }
        }
    }
}

以上无法编译:

error[E0505]: cannot move out of `node` because it is borrowed
  --> src/main.rs:40:17
   |
29 |     fn find_mut(&mut self, key: i32) -> &mut Option<Box<Node>> {
   |                 - let's call the lifetime of this reference `'1`
...
33 |                 Some(box_node) if box_node.key != key => {
   |                      -------- borrow of `node.0` occurs here
...
40 |                 other => return other
   |                 ^^^^^           ----- returning this value requires that `node.0` is borrowed for `'1`
   |                 |
   |                 move out of `node` occurs here

我尝试明确设置生命周期self和输出。我还尝试扩展Some(_)手臂并匹配 forNone而不是other

(编辑 2): 的目的find_mut是将 ref 返回到应该创建新节点的对象(以防找不到密钥)或现有节点所在的位置。

更详细的编译错误的原因是什么?我该怎么去修复它?我正在尝试做的甚至是一个好的做法,(编辑1)即返回参考到修改应该在哪里(假设这不是公共方法)?

标签: rustborrow-checkerownership

解决方案


Rust 编译器之所以会报错,是因为Some(expr)模式匹配整个表达式以其拥有的形式,expr即被移动。

通常,这很容易通过将表达式匹配为借用Some(ref expr)或可变借用来解决Some(ref mut expr),但这里不是这种情况。

如果您查看标准库,您会经常看到as_mut()/ as_ref(),当值可能不存在时,总是返回Option<&mut T>而不是&mut Option<T>。那是因为你真的想访问这个值,而不是数据结构的任何内部结构,就像结构一样Option<Box<None>>

在那之后,我想出了这个:

struct Node {
    key: i32,
    value: i32,
    left: Option<Box<Node>>,
    right: Option<Box<Node>>,
}

struct BinaryTree {
     root: Option<Box<Node>>,
}

impl BinaryTree {
    fn find_mut(&mut self, key: i32) -> Option<&mut Node> {
        // &mut Option<Box<Node>> -> Option<&mut Box<Node>> -> Option<&mut Node>
        let mut node = self.root.as_mut().map(|boxed| boxed.as_mut());
        loop {
            match node {
                Some(box_node) if box_node.key != key => {
                    node = if box_node.key < key {
                        box_node.right.as_mut().map(|boxed| boxed.as_mut())
                    } else {
                        box_node.left.as_mut().map(|boxed| boxed.as_mut())
                    }
                },
                other => return other
            }
        }
    }
}

可能有更好的方法来写这个,但我现在不知道。

请注意,这解决了所有权问题,因为现在&mut Node是移到这里的东西,同时使 API 更好。

至于其是否良好的做法,鉴于其双重含义,是与否。

它可以帮助您学习如何处理借款;另一方面,我们已经有了Vec::binary_search, 和BTreeMap/ BTreeSet,而且很可能crates.io还有其他实现应该涵盖除了最极端的情况之外的所有情况,并且自己制作搜索树几乎没有意义。


推荐阅读