rust - 无法从匹配臂返回对成员的可变引用
问题描述
作为 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)即返回参考到修改应该在哪里(假设这不是公共方法)?
解决方案
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
还有其他实现应该涵盖除了最极端的情况之外的所有情况,并且自己制作搜索树几乎没有意义。