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

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

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

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

Q&A

解決済

1回答

1533閲覧

lifetimeの修飾により変数の寿命を維持したい

namuyan

総合スコア76

Rust

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

0グッド

0クリップ

投稿2020/04/26 12:50

困るのはだいたい以下のようなコードです。

rust

1fn aaa(n) { 2 for a in b { 3 let c = match fnc() { 4 Some(id) => &id, 5 None => &n 6 }; 7 } 8}

fnc()により生成されたidが維持できません。エラー文はerror[E0597]: id does not live long enoughのような形になると思います。よく出現する変数のリファレンスを生成するものの途中で寿命が尽きるというエラーです。

これをlifetimeの修飾によりエラーを回避したいと思います。しかし、どの部分を触ればいいのかわかりません。
何かわかる方は回答の方をよろしくお願いします。

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

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

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

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

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

yumetodo

2020/04/26 14:43

そもそもリファレンスを返すこと自体が間違いだと思われるので、そうするはめになっている背景を追記してください。
guest

回答1

0

ベストアンサー

こんにちは。質問がふわっとしているのでこちらもふわっとしたことしか答えられませんが、それでもよければご覧下さい。

まず、提示されたコードはコンパイルが通らない(引数に型が書かれてない、未定義の変数や関数がある)ので雰囲気から察して修正すると以下のようなコードになります。

rust

1fn aaa(n: i32) { 2 let b = vec![1, 2, 3]; 3 for a in b { 4 let c = match fnc() { 5 Some(id) => &id, 6 None => &n, 7 }; 8 } 9} 10 11fn fnc() -> Option<i32> { 12 Some(0) 13} 14 15fn main() {} 16

これを実行すると確かにE0597が出ます。

error[E0597]: `id` does not live long enough --> lifetime_modifier.rs:5:25 | 4 | let c = match fnc() { | - borrow later stored here 5 | Some(id) => &id, | ^^^- `id` dropped here while still borrowed | | | borrowed value does not live long enough error: aborting due to previous error For more information about this error, try `rustc --explain E0597`.

これをベースに話を進めます。

エラーの原因は、 id のライフタイムがマッチの腕の中で終わるからです。

rust

1 let c = match fnc() { 2 Some(id) => { 3 &id 4 // idのライフタイムはここで終わる 5 }, 6 None => &n, 7 }; 8 // cはidよりも長生きなのでエラー

これは愚直に書くなら fnc の返り値をもう少し長くとるようにすればエラーは出なくなります。

rust

1 // 一旦変数を束縛したのでcより長生きする 2 let tmp = fnc(); 3 // &Option<i32> に対するマッチ 4 let c = match &tmp { 5 // Rustコンパイラが空気を読んでくれるのでidの型は &i32 になる 6 Some(id) => id, 7 None => &n, 8 };

&Option<i32> から &i32 が出てくるのは少しトリッキーなので こちら の記事も参考にしてみて下さい。

上記のコードはref というキーワードを用いてもう少しスッキりした書き方ができます。

rust

1 let c = match fnc() { 2 Some(ref id) => id, 3 None => &n, 4 };

これが推奨される書き方です。

話が脇道に逸れますが、今回のケースに限っていえば Option::as_refOption::_unwrap_orを使ってさらに綺麗に書けます。

rust

1 let c = fnc().as_ref().unwrap_or(&n);

これも覚えておいて損はないでしょう。

さて、今回提示されたコードでは上記の回答で問題ありませんが、状況が変わるとエラーになることがあります。
今回は cfor 式文の中のスコープでのみ有効なので、同じく for 式文の中で生成された fnc の返り値に束縛できました。これが例えば c を関数から返そうとするとエラーになります

rust

1fn aaa(n: i32) -> &i32 { 2 let b = vec![1, 2, 3]; 3 for a in b { 4 let c = fnc().as_ref().unwrap_or(&n); 5 if c == &3 { 6 // cを関数から返そうとする 7 return c; 8 } 9 } 10 &0 11}

この場合は多少記法をいじったところでコンパイルが通りません。
C言語でもありがちな誤りですが、 cfor 式文の中でしか有効でないので、その参照をそれより長生きする関数の外に返そうとしてもエラーになります。
この場合は参照を取らずに値をムーブしてしまうのが正解です。

rust

1// 参照ではなく値そのものを返す 2fn aaa(n: i32) -> i32 { 3 let b = vec![1, 2, 3]; 4 for a in b { 5 let c = match fnc() { 6 Some(id) => id, 7 None => n, 8 }; 9 if c == 3 { 10 return c; 11 } 12 } 13 0 14}

これら2点で恐らく知りたいことはカバーできているとは思いますが、冒頭で述べた通り質問の意図を正確に掴むのが難しいので的外れな回答をしているかもしれません。その場合は遠慮なくおっしゃって下さい。

まとめると以下になります。

  • ref を使うとライフタイムエラーは出なくなる
  • ただしこれも場合によるので、場合によっては参照をとらないことも必要
  • 的確な回答を得たいなら正確なコードで質問すべき

以上、参考になれば幸いです。

投稿2020/04/27 07:59

blackenedgold

総合スコア468

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

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

namuyan

2020/04/27 15:06 編集

回答していただきありがとうございます!~~`&`は`id`から借用を、`ref`は`fnc()`から借用をしている、という事でよろしいでしょうか?~~ いや、そう書いてありますね、ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問