質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

1回答

605閲覧

optionをmatchで取得する時にnoneが返される場合の借用範囲

namuyan

総合スコア76

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2020/05/10 08:42

下記のコードは、HashMapより参照を取得しようとし、無い場合はデータを生成して挿入した後に参照を取得する、という関数です。Metaが重いオブジェクトの場合に用いられそうな構文です。

rust

1pub struct Asker { 2 cache: HashMap<U256, Meta>, 3} 4 5impl Asker { 6 fn get_ref(&mut self, hash: &U256, tasks: &Tasks) -> Result<&Meta, String> { 7 match self.cache.get(hash) { 8 Some(meta) => Ok(meta), 9 10 None => match tasks.get_data(hash)? { 11 Some(data) => { 12 let meta= Meta::from(data); 13 self.cache.insert(hash.clone(), meta); 14 15 if 100 <= self.cache.len() { 16 let (delete_hash, _) = self 17 .cache 18 .iter() 19 .min() 20 .unwrap(); 21 let delete_hash = delete_hash.clone(); 22 self.cache.remove(&delete_hash); 23 } 24 25 Ok(self.cache.get(&hash).unwrap()) 26 }, 27 None => Err("failed".to_owned()), 28 }, 29 } 30 } 31}

これは借用チェッカーによりエラーになります。optionでnoneが返される部分のブロックではsomeでの借用は影響されないはずです。しかし、下記のエラーを読み取ると影響があるように思われます。

error

1error[E0502]: cannot borrow `self.cache` as mutable because it is also borrowed as immutable 2 --> src\nice\man.rs:79:25 3 | 452 | fn get_ref(&mut self, hash: &U256, tasks: &Tasks) -> Result<&Meta, String> { 5 | - let's call the lifetime of this reference `'1` 6 | 754 | match self.cache.get(hash) { 8 | ---------- immutable borrow occurs here 955 | Some(meta) => Ok(meta), 10 | ---------- returning this value requires that `self.cache` is borrowed for `'1` 11... 1279 | self.cache.remove(&delete_hash); 13 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

この借用の範囲について、またはエラーの回避方法について何かわかる方は回答の方をよろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

エラーメッセージによると、最初のmatchアームSome(meta) => Ok(meta)self.cacheが所有する値への参照&Metaをmatch式の外に返しているのが原因のようです。&Metaが有効な間、つまり、get_ref()メソッドの外までself.cacheの不変借用が続くことになるので、たとえNoneのアーム内であっても、その不変借用が有効になっています。

なぜこうなるのかというと、get_ref()から戻った後、&Metaが有効な間はcacheマップの内容を変更できないようにするためです。もし変更を許すと、&Metaが指しているMetacacheマップから削除できてしまい、&Metaはダングリングポインタになってしまいます。

問題となっているのはOk(meta)を返すところです。その部分がself.cache.remove()などと競合しないようにメソッドの最後に移せばエラーが解消します。

rust

1 fn get_ref(&mut self, hash: &U256, tasks: &Tasks) -> Result<&Meta, String> { 2 // Metaの存在チェックをして、存在しないならcacheに追加する 3 if !self.cache.contains_key(hash) { 4 match tasks.get_data(hash)? { 5 Some(data) => { 6 let meta = Meta::from(data); 7 self.cache.insert(hash.clone(), meta); 8 9 if 100 <= self.cache.len() { 10 let (delete_hash, _) = self.cache.iter().min().unwrap(); 11 let delete_hash = delete_hash.clone(); 12 self.cache.remove(&delete_hash); 13 } 14 } 15 None => return Err("failed".to_owned()), 16 } 17 } 18 19 // cacheしておいたMetaを返す。これ以降は`&Mata`が有効な間は`self.cache`は 20 // 変更できなくなる 21 self.cache.get(hash).ok_or_else(|| "failed".to_owned()) 22 }

投稿2020/05/10 14:15

編集2020/05/10 16:04
tatsuya6502

総合スコア2046

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namuyan

2020/05/11 07:28

なるほど... やはり参照を返す操作は難易度が高いですね、でも理解できました。ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問