简体   繁体   English

在“if let Some(ref mut x) = option”和“if let Some(x) = option.as_mut()”中匹配可变选项引用有什么区别?

[英]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()”?

Background背景

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

Question问题

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 的初学者)我希望这些匹配表达式是等价的,但显然我缺少一些细微的差异。

Update更新

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

Update 2更新 2

(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.由于xtail的借用, &mut x.next也是tail的借用,这意味着tail = &mut x.nexttail借用本身。 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::Somex )。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM