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

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

新規登録して質問してみよう
ただいま回答率
85.48%
参照

参照は、プログラミングにおいて変数や関数といったメモリ空間上での所在を指示するデータのことを指します。その中にはデータ自体は含まれず、他の場所にある情報を間接的に指示するプログラムです。

コンパイルエラー

コンパイルのフェーズで生成されるエラーです。よく無効なシンタックスやタイプが含まれているとき発生します。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Rust

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

Q&A

解決済

1回答

2461閲覧

cannot return value referencing local variable を解決したい

sutonea

総合スコア207

参照

参照は、プログラミングにおいて変数や関数といったメモリ空間上での所在を指示するデータのことを指します。その中にはデータ自体は含まれず、他の場所にある情報を間接的に指示するプログラムです。

コンパイルエラー

コンパイルのフェーズで生成されるエラーです。よく無効なシンタックスやタイプが含まれているとき発生します。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Rust

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

0グッド

0クリップ

投稿2022/07/23 04:53

前提

商品一覧CSV から、指定した id をもつ行を検索するプログラムを作っています。
現在、検索結果がもつ文字列から商品を表すオブジェクトを生成し、それを関数の戻り値にようとしています。

実現したいこと

コンパイルエラーを解消し、 find_by_idMarchant を返すようにしたいです。

発生している問題・エラーメッセージ

Compiling playground v0.0.1 (/playground) error[E0515]: cannot return value referencing local variable `r` --> src/main.rs:47:9 | 35 | let name = &r[1]; | - `r` is borrowed here ... 47 | found | ^^^^^ returns a value referencing data owned by the current function For more information about this error, try `rustc --explain E0515`. error: could not compile `playground` due to previous error

該当のソースコード

Playground

rust

1use csv; 2 3/// 商品 4struct Marchant<'a> { 5 id: &'a str, // id 6 name: &'a str, // 名前 7 price: i32, // 価格 8} 9 10impl<'a> Marchant<'a> { 11 fn new(id: &'a str, name: &'a str, price: i32) -> Self { 12 Self { id, name, price} 13 } 14} 15 16/// 商品検索機 17struct MarchantFinder<'b> { 18 /// CSV 19 csv_reader: csv::Reader<&'b [u8]>, 20} 21 22impl<'b> MarchantFinder<'b> { 23 24 /// CSV から、指定した id を持つ商品を取得する 25 fn find_by_id<'a>(&'a mut self, id: &'a str) -> Option<Marchant> { 26 let mut found: Option<Marchant> = None; 27 28 // csv から id を探す 29 for record in self.csv_reader.records() { 30 found = match record { 31 Ok(r) => { 32 let current_id = &r[0]; 33 if current_id == id { 34 // id が見つかった場合、その行の 名前 と 価格 から 商品を生成する 35 let name = &r[1]; 36 let price = (&r[2]).parse().unwrap(); 37 let marchant = Marchant::new(id, name, price); 38 found = Some(marchant); 39 } 40 found 41 } 42 Err(_) => { // id が見つからなかった場合 43 None 44 } 45 }; 46 } 47 found 48 } 49} 50 51fn main() { 52 let str_csv = "id,name,price 53 101,Apple,150 54 102,Orange,80, 55 103,Banana,30"; 56 let reader = csv::Reader::from_reader(str_csv.as_bytes()); 57 let mut finder = MarchantFinder { csv_reader: reader }; 58 59 let id = "102"; // 検索対象の id 60 match finder.find_by_id(id) { 61 Some(marchant) => { 62 println!("id: {}, name:{} price:{}", id, marchant.name, marchant.id); 63 } 64 None => { 65 println!("id: No marchant"); 66 } 67 }; 68} 69

補足情報(FW/ツールのバージョンなど)

Build using the Stable version: 1.62.1

どうぞよろしくお願いいたします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

&str はスライスです。 どこかにある文字列の実体を指し示すオブジェクトなので元になる文字列より長い寿命を持つことが出来ません。 そしてこの場合は r が文字列の実体を所有しているので r の寿命が先に尽きるとそれを指し示しているスライスが意味をなさなくなります。 故にライフタイムの関係が破綻してエラーになっています。

実際に文字列が消滅しているのでライフタイムの指定をどう弄ってもどうにもなりません。 文字列の所有権をどのように移動するのかを考慮したデザインにしなおしてください。

csv というクレートは入力が文字列ではない場合 (ファイルなど) もサポートしているのでレコード単位で文字列を (構築して一時的に) 所有するという構造になっているのでしょう。 質問中のコードでは &r[0]str_csv (の一部) を指すスライスではなく r が所有する文字列を指すスライスが返されているということを意識してください。


状況によって色々と戦略はあるとは思いますが、例としてひとつ挙げるとすれば文字列からレコードの配列を構築してしまうことです。 全体の情報が集積されたオブジェクトを作り、各種の操作はそれを参照するという形です。

rust

1use csv; 2 3struct Merchant<'a> { 4 id: &'a str, 5 name: &'a str, 6 price: i32, 7} 8 9impl<'a> std::fmt::Display for Merchant<'a> { 10 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 11 write!( 12 f, 13 "id: {}, name:{} price:{}", 14 self.id, self.name, self.price 15 ) 16 } 17} 18 19impl<'a> Merchant<'a> { 20 fn new(id: &'a str, name: &'a str, price: i32) -> Self { 21 Self { id, name, price } 22 } 23} 24 25trait MerchantFinder<'a> { 26 fn find_by_id(&'a self, id: &str) -> Option<Merchant<'a>>; 27} 28 29impl<'a> MerchantFinder<'a> for std::vec::Vec<csv::StringRecord> { 30 fn find_by_id(&'a self, id: &str) -> Option<Merchant<'a>> { 31 let found = self.iter().find(|&x| &x[0] == id); 32 33 match found { 34 Some(&ref r) => { 35 let name = &r[1]; 36 let price = (&r[2]).parse().unwrap(); 37 let merchant = Merchant::new(&r[0], name, price); 38 Some(merchant) 39 } 40 _ => None, 41 } 42 } 43} 44 45use std::iter::Iterator; 46use std::vec::Vec; 47 48fn main() -> Result<(), csv::Error> { 49 let str_csv = "id,name,price\n\ 50 101,Apple,150\n\ 51 102,Orange,80\n\ 52 103,Banana,30"; 53 54 // 全部の情報が格納されたオブジェクト 55 let records = csv::Reader::from_reader(str_csv.as_bytes()) 56 .records() 57 .collect::<Result<Vec<csv::StringRecord>, csv::Error>>()?; 58 let id = "102"; // 検索対象の id 59 60 match records.find_by_id(id) { 61 Some(merchant) => { 62 println!("{}", merchant); 63 } 64 None => { 65 println!("id: No merchant"); 66 } 67 }; 68 Ok(()) 69}

ここでは全ての情報をメモリ上に持つという戦略を提示しましたが、一度しか情報を走査しないということがわかっている状況などであれば Merchant が文字列の本体を持つようにするという方法でもよいでしょう。

投稿2022/07/23 07:36

編集2022/07/23 13:48
SaitoAtsushi

総合スコア5444

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

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

sutonea

2022/07/23 21:34

文字列の保持方法に関する問題点のご指摘と、コード例のご提示ありがとうございます!Merchant が文字列の本体を持つ方法も試そうかと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問