[英]What is the difference between matching a mutable Option reference in “if let Some(ref mut x) = option” and in “if let Some(x) = option.as_mut()”?
Consider a toy problem in which I have a Node
struct that represents nodes of linked lists and that I want to create a function that builds a list with values from 1 to 9. The following code works as expected:考虑一个玩具问题,其中我有一个表示链表节点的
Node
结构,并且我想创建一个 function 来构建一个值从 1 到 9 的列表。以下代码按预期工作:
struct Node {
val: i32,
next: Option<Box<Node>>,
}
fn build_list() -> Option<Box<Node>> {
let mut head = None;
let mut tail = &mut head;
for n in 1..10 {
*tail = Some(Box::new(Node {val: n, next: None}));
if let Some(ref mut x) = tail {
tail = &mut x.next;
};
}
head
}
But if I modify the match expression in the build_list
function to the following, it fails to compile:但是,如果我将
build_list
function 中的匹配表达式修改为以下内容,则无法编译:
fn build_list() -> Option<Box<Node>> {
let mut head = None;
let mut tail = &mut head;
for n in 1..10 {
*tail = Some(Box::new(Node {val: n, next: None}));
if let Some(x) = tail.as_mut() {
tail = &mut x.next;
};
}
head
}
The compilation error:编译错误:
error[E0506]: cannot assign to `*tail` because it is borrowed
--> src/main.rs:72:9
|
72 | *tail = Some(Box::new(Node {val: n, next: None}));
| ^^^^^
| |
| assignment to borrowed `*tail` occurs here
| borrow later used here
73 | {
74 | if let Some(x) = tail.as_mut() {
| ---- borrow of `*tail` occurs here
error[E0499]: cannot borrow `*tail` as mutable more than once at a time
--> src/main.rs:74:30
|
74 | if let Some(x) = tail.as_mut() {
| ^^^^ mutable borrow starts here in previous iteration of loop
In this example, what is the difference between在这个例子中,有什么区别
if let Some(ref mut x) = tail
and和
if let Some(x) = tail.as_mut()
? ?
(As a beginner learning Rust) I was expecting those match expressions to be equivalent, but apparently there's some subtle difference that I'm missing. (作为学习 Rust 的初学者)我希望这些匹配表达式是等价的,但显然我缺少一些细微的差异。
I cleaned up the code from my original example so that I don't need a placeholder element for the head of the list.我清理了原始示例中的代码,这样我就不需要列表头部的占位符元素了。 The difference (and compilation error) still remains, I just get an additional compilation error for assigning to borrowed
*tail
.差异(和编译错误)仍然存在,我只是得到一个额外的编译错误来分配给借来的
*tail
。
(This is just a curious observation, doesn't help answering the original question) After considering @Emoun's answer, it sounded important (in the first working example) that the compiler should be able to know that tail
was changing at every iteration of the loop (such that it could make sure &mut x.next
being borrowed each time was different). (这只是一个奇怪的观察,无助于回答原始问题)在考虑了@Emoun 的回答之后,编译器应该能够知道
tail
在每次迭代时都在变化,这听起来很重要(在第一个工作示例中)循环(这样它可以确保每次借用的&mut x.next
都是不同的)。 So I made an experiment to change the code in a way that the compiler wouldn't be able to tell if that was the case by adding a if n % 2 == 0
condition to the tail = &mut x.next;
所以我做了一个实验,通过在
tail = &mut x.next;
中添加if n % 2 == 0
条件来改变代码,编译器无法判断是否是这种情况。 assignment.任务。 Sure enough, it resulted in a compilation error similar to the other one:
果然,它导致了与另一个类似的编译错误:
fn build_list() -> Option<Box<Node>> {
let mut head = None;
let mut tail = &mut head;
for n in 1..10 {
*tail = Some(Box::new(Node {val: n, next: None}));
if let Some(ref mut x) = tail {
if n % 2 == 0 {
tail = &mut x.next;
}
};
}
head
}
The new error:新错误:
error[E0506]: cannot assign to `*tail` because it is borrowed
--> src/main.rs:60:9
|
60 | *tail = Some(Box::new(Node {val: n, next: None}));
| ^^^^^
| |
| assignment to borrowed `*tail` occurs here
| borrow later used here
61 | if let Some(ref mut x) = tail {
| --------- borrow of `*tail` occurs here
error[E0503]: cannot use `*tail` because it was mutably borrowed
--> src/main.rs:61:16
|
61 | if let Some(ref mut x) = tail {
| ^^^^^---------^
| | |
| | borrow of `tail.0` occurs here
| use of borrowed `tail.0`
| borrow later used here
error[E0499]: cannot borrow `tail.0` as mutable more than once at a time
--> src/main.rs:61:21
|
61 | if let Some(ref mut x) = tail {
| ^^^^^^^^^ mutable borrow starts here in previous iteration of loop
The reason why the second version of your code fails is because rust's methods/function always borrow whole objects and never parts of them.你的代码的第二个版本失败的原因是因为 rust 的方法/函数总是借用整个对象,而不是它们的一部分。
What this means in your case is that the tail.as_mut()
borrows tail
mutably and that this borrow will stay in effect as long as tail
is being used:在您的情况下,这意味着
tail.as_mut()
可变地借用tail
,只要使用tail
,这种借用就会保持有效:
...
for n in 1..10 {
*tail = Some(Box::new(Node {val: n, next: None})); // Error in the second iteration,
// 'tail' was already borrowed
if let Some(x) = tail.as_mut() { // <-+ Borrow of 'tail' starts in the first iteration
tail = &mut x.next; // <-+ 'tail' now borrows itself
}; // |
} // <-+ Borrow of 'tail' ends here, after the last iteration
...
Since x
is a borrow of tail
, &mut x.next
is also a borrow of tail
, which means tail = &mut x.next
is tail
borrowing itself.由于
x
是tail
的借用, &mut x.next
也是tail
的借用,这意味着tail = &mut x.next
是tail
借用本身。 Therefore, the initial borrow of tail
cannot go out of scope as long as tail
is in scope.因此,
tail
的初始借用不能 go 超出 scope,只要tail
在 scope 中。 tail
is used in every iteration, so the borrow can only go out of scope after the last iteration of the loop.每次迭代都使用
tail
,因此在循环的最后一次迭代之后,借位只能从scope中借出go。
Now, why does the first version of build_list
work?现在,为什么第一个版本的
build_list
可以工作? In short: because tail
is never borrowed.简而言之:因为
tail
永远不会被借来。 if let Some(ref mut x) = tail
is a destructuring of tail
into its components (in this case the Option::Some
and x
). if let Some(ref mut x) = tail
是将tail
解构为其组件(在本例中为Option::Some
和x
)。 This doesn't borrow tail
as a whole, it just borrows x
.这不是作为一个整体借用
tail
,它只是借用x
。 When you then tail = &mut x.next
, you now also destructure x
into its components (extracting only next
), and borrow that using tail
.然后当您使用
tail = &mut x.next
时,您现在还将x
解构为其组件(仅提取next
),并使用tail
借用它。 In the next iteration, tail
is not borrowed and can therefore happily be reassigned.在下一次迭代中,
tail
不会被借用,因此可以很高兴地重新分配。
Method/function calls are limited in that they don't know which parts of an object you will use later.方法/函数调用受到限制,因为它们不知道您稍后将使用 object 的哪些部分。 Therefore,
as_mut()
has to borrow the whole tail
, even though you are only using part of it.因此,
as_mut()
必须借用整个tail
,即使您只使用它的一部分。 This is a limitation with the type system and is one reason why getter/setter methods are weaker/more limiting than calling a struct's members directly: getters/setters will force you to borrow the whole struct, while accessing a member directly will only borrow that member and not the others.这是类型系统的限制,也是 getter/setter 方法比直接调用结构的成员更弱/更受限制的原因之一:getter/setter 将强制您借用整个结构,而直接访问成员只会借用它成员而不是其他人。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.