2016-06-25 1 views
9

私は、借りようとしている間に不変の借りをしているとRustが告げるところで永続的なコンパイルエラーが出ますが、不変の借りは別のスコープから来ています。HashMapのRust Borrowは、それが入っているスコープを超えていますか?

私はマップ内の値をチェックするコードを持っています。コードが存在する場合はそれを返します。そうでなければマップをさまざまな方法で変更する必要があります。問題は、2つの操作が完全に分離しているにもかかわらず、私が両方をやらせる方法を見つけられないように見えることです。これは、私にはどんな意味がありません

error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable 
    --> src/main.rs:17:5 
    | 
7 |   match map.get(&key) { // borrow immutably 
    |    --- immutable borrow occurs here 
... 
17 |  map.insert(0, 0); // borrow mutably, which errors 
    |  ^^^ mutable borrow occurs here 
18 |  None 
19 | } 
    | - immutable borrow ends here 

fn do_stuff(map: &mut BTreeMap<i32, i32>, key: i32) -> Option<&i32> { 
    // extra scope in vain attempt to contain the borrow 
    { 
     match map.get(&key) { // borrow immutably 
      Some(key) => { return Some(key); }, 
      None =>(), 
     } 
    } 

    // now I'm DONE with the immutable borrow, but rustc still thinks it's borrowed 

    map.insert(0, 0); // borrow mutably, which errors 
    None 
} 

このエラーが出持つ:

は、ここに私のコードと同じ構造に従っており、問題を示すいくつかの無意味なコードです。不変の借り方はどのようにその範囲よりも長く続くのですか?そのmatchの1つのブランチはreturnを介して関数を終了し、もう1つは何もせずスコープから出ます。

これは私が間違って他の変数のスコープから借りたものを密輸してしまった前に起こっていましたが、ここにはありません!

真実では、借用はreturnステートメントでスコープをエスケープしていますが、そのブロックが関数内でさらに借りることをブロックすることは馬鹿げています。私がそこに何か他のものを返すと、エラーは消えてしまいます。だから私はこれが借りチェッカーがハングアップしていると思います。これは私のバグのように感じます。

残念ながら、私は同じエラーが発生することなくこれを書き換える方法を見つけることができませんでした。その場合、特に厄介なバグです。

+0

残念ながら、.ENTRYは()この関数が行う必要がある何のために右ではありません。私は非字句スコープの問題を認識していますが、通常はその問題を回避することができますが、この場合、どのように重複した作業を行わないものを考え出すことができませんでした回避策は醜いです...また、通常はスコープを追加することで問題を回避できますが、ここではそうではありません。 1つの借りを別の機能に移動しても役立たない。 –

答えて

6

これは、non-lexical scopesによって解決される可能性が非常に高いknown issueであり、それ自体がMIRを前提としています。あなたが探しているのと同じ鍵に挿入している場合は、use the entry APIをお勧めします。

これを回避するために、非能率のsmidgenを追加できます。一般的な考え方は、値が存在するかどうかを示すブール値を追加することです。このブール値は、基準にハングアップしないので、何も借りはない。代わりHashMapのベクトルと

use std::collections::BTreeMap; 

fn do_stuff(map: &mut BTreeMap<i32, i32>, key: i32) -> Option<&i32> { 
    if map.contains_key(&key) { 
     return map.get(&key); 
    } 

    map.insert(0, 0); 
    None 
} 

fn main() { 
    let mut map = BTreeMap::new(); 
    do_stuff(&mut map, 42); 
    println!("{:?}", map) 
} 

類似事例は、要素のインデックスの代わりに参照を使用することによって解決することができます。上記の場合と同様に、スライス境界を再度チェックする必要があるため、これは少しの非効率性をもたらす可能性があります。代わりに、あなたが書くことができ

fn find_or_create_five<'a>(container: &'a mut Vec<u8>) -> &'a mut u8 { 
    match container.iter_mut().find(|e| **e == 5) { 
     Some(element) => element, 
     None => { 
      container.push(5); 
      container.last_mut().unwrap() 
     } 
    } 
} 

:非語彙的寿命が取り組まれていることを

fn find_or_create_five<'a>(container: &'a mut Vec<u8>) -> &'a mut u8 { 
    let idx = container.iter().position(|&e| e == 5).unwrap_or_else(|| { 
     container.push(5); 
     container.len() - 1  
    }); 
    &mut container[idx] 
} 

注意。毎晩錆では、両方のオリジナルのコード例は、NLLが有効になっている場合であるとしてコンパイル:

#![feature(nll)] 

use std::collections::BTreeMap; 

fn do_stuff(map: &mut BTreeMap<i32, i32>, key: i32) -> Option<&i32> { 
    if let Some(key) = map.get(&key) { 
     return Some(key); 
    } 

    map.insert(0, 0); 
    None 
} 
#![feature(nll)] 

fn find_or_create_five(container: &mut Vec<u8>) -> &mut u8 { 
    match container.iter_mut().find(|e| **e == 5) { 
     Some(element) => element, 
     None => { 
      container.push(5); 
      container.last_mut().unwrap() 
     } 
    } 
} 
関連する問題