Rust 为什么在元组模式匹配中需要显式借用?
我正在用Rust写一棵二叉树,借阅检查器真的把我弄糊涂了。这是 二叉树的定义如下:Rust 为什么在元组模式匹配中需要显式借用?,rust,pattern-matching,move-semantics,Rust,Pattern Matching,Move Semantics,我正在用Rust写一棵二叉树,借阅检查器真的把我弄糊涂了。这是 二叉树的定义如下: struct NonEmptyNode; pub struct BinaryTree { root: Option<NonEmptyNode>, } 错误消息是 错误[E0507]:无法移出借用的内容 -->src/main.rs:10:16 | 10 |匹配(self.root、bt.root){ |^^^^无法移出借用的内容 错误[E0507]:无法移出借用的内容 -->src/mai
struct NonEmptyNode;
pub struct BinaryTree {
root: Option<NonEmptyNode>,
}
错误消息是
错误[E0507]:无法移出借用的内容
-->src/main.rs:10:16
|
10 |匹配(self.root、bt.root){
|^^^^无法移出借用的内容
错误[E0507]:无法移出借用的内容
-->src/main.rs:10:27
|
10 |匹配(self.root、bt.root){
|^^无法移出借用的内容
要使其工作,必须将代码修改为:
// Implementation #2
fn set_child_helper(&self, bt: &Self, setter: fn(&NonEmptyNode, &NonEmptyNode)) -> bool {
match (&self.root, &bt.root) {
// explicit borrow
(&Some(ref rt), &Some(ref node)) => {
// explicit borrow
setter(rt, node);
true
}
_ => false,
}
}
如果在没有显式借用的情况下,我一次匹配一个变量,借用检查器根本不会抱怨:
// Implementation #3
fn set_child_helper(&self, bt: &Self, setter: fn(&NonEmptyNode, &NonEmptyNode)) -> bool {
match self.root {
Some(ref rt) => match bt.root {
// No explict borrow will be fine
Some(ref node) => {
// No explicit borrow will be fine
setter(rt, node);
true
}
_ => false,
},
_ => false,
}
}
为什么实现#3不需要显式借用,而实现#1需要显式借用?关键在于
self.root
和bt.root
是s,而元组不是。之所以#3有效,是因为编译器知道如何“通过”中间表达式来绑定到原始存储位置
从另一个角度来看:非常简单的表达式,如self.root
,其特殊之处在于它们的外观和行为类似于值(并且具有值类型),但编译器会秘密地记住它是如何达到该值的,允许它返回并获取指向该值读取位置的指针
判断某物是否为“位置表达式”的简单方法是尝试为其赋值。如果可以执行expr=some_value;
,则expr
必须为“位置表达式”。顺便说一句,这也是为什么在编写&self.root
时,会得到指向存储self.root
的位置的指针,而不是指向self.root
副本的指针
这个“地方表达”业务不适用于元组,因为元组没有此属性。要构造元组,编译器必须实际读取元组元素的值,并将其移动或复制到元组的新存储中。这会破坏编译器可能具有的任何位置关联:这些值不再是以前的位置
最后,您可能想看看
选项::as_ref
,它将&Option
转换为选项
。这将允许您匹配(self.root.as_ref()、bt.root.as_ref())
,模式类似于(一些(rt),一些(node))
,这可能更方便。关键是self.root
和bt.root
是s,而元组不是。之所以#3有效,是因为编译器知道如何“遍历”中间表达式以绑定到原始存储位置
从另一个角度来看:非常简单的表达式,如self.root
,其特殊之处在于它们的外观和行为类似于值(并且具有值类型),但编译器会秘密地记住它是如何达到该值的,允许它返回并获取指向该值读取位置的指针
判断某物是否为“位置表达式”的简单方法是尝试为其赋值。如果可以执行expr=some_value;
,则expr
必须为“位置表达式”。顺便说一句,这也是为什么在编写&self.root
时,会得到指向存储self.root
的位置的指针,而不是指向self.root
副本的指针
这个“地方表达”业务不适用于元组,因为元组没有此属性。要构造元组,编译器必须实际读取元组元素的值,并将其移动或复制到元组的新存储中。这会破坏编译器可能具有的任何位置关联:这些值不再是以前的位置
最后,您可能想看看选项::as_ref
,它将&Option
转换为选项
。这将允许您匹配(self.root.as_ref()、bt.root.as_ref())
,模式类似于(一些(rt),一些(node))
,这可能更方便。巧合的是,在你发布这个问题几个小时后,发布了这个问题,其中包括(除其他酷的事情外)自动引用和取消引用匹配模式的语义。这意味着当匹配(&self.root,&bt.root)
时,你可以编写(一些(rt),一些(node))
,编译器将智能地将rt
和node
转换为引用。()巧合的是,在你发布这个问题几小时后,发布了这个问题,其中包括(除其他酷的东西外)匹配
模式的自动引用和取消引用语义。这意味着当匹配(&self.root,&bt.root)
时,您可以编写(一些(rt),一些(节点))
,编译器将智能地将rt
和节点
转换为引用。()
// Implementation #3
fn set_child_helper(&self, bt: &Self, setter: fn(&NonEmptyNode, &NonEmptyNode)) -> bool {
match self.root {
Some(ref rt) => match bt.root {
// No explict borrow will be fine
Some(ref node) => {
// No explicit borrow will be fine
setter(rt, node);
true
}
_ => false,
},
_ => false,
}
}