2016-11-17 27 views
1

如何在沒有Rust的情況下訪問選項中的矢量在第一次訪問時移動它?如何在「選項」中多次訪問矢量?

fn maybe_push(v_option: Option<&mut Vec<usize>>) -> usize { 
    let mut c = 0; 
    if let Some(v) = v_option { 
     for i in 0..10 { 
      v.push(i); 
      c += i; 
     } 
    } else { 
     for i in 0..10 { 
      c += i; 
     } 
    } 

    // second access, fails 
    if let Some(v) = v_option { 
     for i in 10..20 { 
      v.push(i); 
      c += i; 
     } 
    } else { 
     for i in 10..20 { 
      c += i; 
     } 
    } 

    return c; 
} 


fn main() { 
    let mut v: Vec<usize> = vec![]; 

    println!("{}", maybe_push(Some(&mut v))); 
    println!("{}", maybe_push(None)); 

    println!("{:?}", v); 
} 

這給了錯誤:由價值

error: cannot borrow immutable anonymous field `(v_option:std::prelude::v1::Some).0` as mutable 
--> src/main.rs:4:21 
    | 
4 |   if let Some(ref mut v) = v_option { 
    |      ^^^^^^^^^ 

error: cannot borrow immutable anonymous field `(v_option:std::prelude::v1::Some).0` as mutable 
    --> src/main.rs:17:21 
    | 
17 |   if let Some(ref mut v) = v_option { 
    |      ^^^^^^^^^ 

回答

6

匹配移動值入模式變量:

error[E0382]: use of partially moved value: `v_option` 
    --> src/main.rs:16:22 
    | 
4 |  if let Some(v) = v_option { 
    |     - value moved here 
... 
16 |  if let Some(v) = v_option { 
    |      ^^^^^^^^ value used here after move 

使用if let Some(ref mut v) = v_option {已經提出的失敗了。移動會使原始值無法使用,除了實現Copy特徵的非常簡單的對象(如數字)外。不像在C指針,可變的引用不能夠複製,它可以在下面的例子中可以看出,不編譯或者:

let mut v = vec![1, 2, 3]; 
let rv = &mut v; // mutable reference to v 
{ 
    // move rv to r1, r1 now becomes the sole mutable reference to v 
    let r1 = rv; 
    r1.push(4); 
} 
{ 
    let r2 = rv; // error: rv was already moved to r1 
    r2.push(5); 
} 

鏽病拒絕上述因爲它強制的一般規則禁止向一個對象的多個可變的引用。儘管這個特定的代碼片段是安全的,但允許同時對同一個對象進行多次可變引用可以很容易地編寫Rust明確設計用於防止的不安全程序。那些在多線程代碼中包含數據競爭的應用程序,或通過無效迭代器訪問數據的應用程序。因此,轉讓let r1 = rv只能移動rv參考r1,不允許let r2 = rv聲明,它現在指的是移動變量rv

要修復代碼,我們必須創建單獨的引用以引用。這將創建原始參考兩個不同的可變的引用,而不是移動所述原始可變引用到內部範圍:

let mut v = vec![1, 2, 3]; 
let mut rv = &mut v; 
{ 
    // rr1 is a *new* mutable reference to rv - no move is performed 
    let rr1 = &mut rv; 
    rr1.push(4); 
} 
{ 
    // rr2 is a *separate* new mutable reference to rv - also no move 
    let rr2 = &mut rv; 
    rr2.push(5); 
} 

push調用的語法與r1.pushrr1.push因爲鏽病的.操作者將自動解除引用任何相同參考數量。

要從問題返回到例如,在Option提及Vec<usize>類似於上面的v參考,以及使用該Some(v)模式匹配它移動的參考成v圖案的變量。

爲了解決這個問題,必須像上面的例子那樣改變模式,以指定一個變量,引用爲本身爲參考的值。這是通過使用if let Some(ref mut v)語法來實現的。如上所述,在rv聲明必須更改爲mutable的情況下,此修補程序還要求Option是可變的。與這兩個變化,代碼編譯:

fn maybe_push(mut v_option: Option<&mut Vec<usize>>) -> usize { 
    let mut c = 0; 
    if let Some(ref mut v) = v_option { 
     for i in 0..10 { 
      v.push(i); 
      c += i; 
     } 
    } else { 
     for i in 0..10 { 
      c += i; 
     } 
    } 

    if let Some(ref mut v) = v_option { 
     for i in 10..20 { 
      v.push(i); 
      c += i; 
     } 
    } else { 
     for i in 10..20 { 
      c += i; 
     } 
    } 

    return c; 
} 

fn main() { 
    let mut v: Vec<usize> = vec![]; 

    println!("{}", maybe_push(Some(&mut v))); 
    println!("{}", maybe_push(None)); 

    println!("{:?}", v); 
} 

另一種可能性是使用as_mut()方法來選擇內容返回作爲可變引用先前的內容。這在概念上等同於在第一個片段中從let r1 = rvlet rr1 = &mut rv的變化,並且將允許使用if let Some(v)模式,其中v仍然是對可變引用的引用。這也要求v_option被聲明爲可變的。

+2

在書中:https://doc.rust-lang.org/book/patterns.html#ref-and-ref-mut(非常非常簡潔)。 –

+1

@Shepmaster很好的選擇,謝謝。我現在已經更新了包含它的答案,以及關於原始移動不起作用的附加說明。 – user4815162342