簡體   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()”?

背景

考慮一個玩具問題,其中我有一個表示鏈表節點的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
}

但是,如果我將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
}

編譯錯誤:

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

問題

在這個例子中,有什么區別

if let Some(ref mut x) = tail

if let Some(x) = tail.as_mut()

?

(作為學習 Rust 的初學者)我希望這些匹配表達式是等價的,但顯然我缺少一些細微的差異。

更新

我清理了原始示例中的代碼,這樣我就不需要列表頭部的占位符元素了。 差異(和編譯錯誤)仍然存在,我只是得到一個額外的編譯錯誤來分配給借來的*tail

更新 2

(這只是一個奇怪的觀察,無助於回答原始問題)在考慮了@Emoun 的回答之后,編譯器應該能夠知道tail在每次迭代時都在變化,這聽起來很重要(在第一個工作示例中)循環(這樣它可以確保每次借用的&mut x.next都是不同的)。 所以我做了一個實驗,通過在tail = &mut x.next;中添加if n % 2 == 0條件來改變代碼,編譯器無法判斷是否是這種情況。 任務。 果然,它導致了與另一個類似的編譯錯誤:

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
}

新錯誤:

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

你的代碼的第二個版本失敗的原因是因為 rust 的方法/函數總是借用整個對象,而不是它們的一部分。

在您的情況下,這意味着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
...

由於xtail的借用, &mut x.next也是tail的借用,這意味着tail = &mut x.nexttail借用本身。 因此, tail的初始借用不能 go 超出 scope,只要tail在 scope 中。 每次迭代都使用tail ,因此在循環的最后一次迭代之后,借位只能從scope中借出go。

現在,為什么第一個版本的build_list可以工作? 簡而言之:因為tail永遠不會被借來。 if let Some(ref mut x) = tail是將tail解構為其組件(在本例中為Option::Somex )。 這不是作為一個整體借用tail ,它只是借用x 然后當您使用tail = &mut x.next時,您現在還將x解構為其組件(僅提取next ),並使用tail借用它。 在下一次迭代中, tail不會被借用,因此可以很高興地重新分配。

方法/函數調用受到限制,因為它們不知道您稍后將使用 object 的哪些部分。 因此, as_mut()必須借用整個tail ,即使您只使用它的一部分。 這是類型系統的限制,也是 getter/setter 方法比直接調用結構的成員更弱/更受限制的原因之一:getter/setter 將強制您借用整個結構,而直接訪問成員只會借用它成員而不是其他人。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM