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

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

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

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

4967閲覧

Rust言語のCSVファイルを読み込む練習問題を解決したい

sanagif

総合スコア1

Rust

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/05/24 15:28

以下のPythonコードに相当するものをRust言語で書こうとしています。

python

1data = [] 2for line in open('data.csv'): 3 data.append(line.split(',')) 4 5print('input row number: ', end='') 6r = int(input()) 7print('input column number: ', end='') 8c = int(input()) 9 10print(data[r][c])

data.csvを1行ずつ読み込んで,2次元のリストに格納し,入力された行番号・列番号に対応するデータを出力するプログラムです。
以下のようなRustコードを書きましたがRustの所有権の問題で当然コンパイルエラーとなります。

rust

1use std::fs::File; 2use std::io::{BufRead, BufReader}; 3 4fn main() -> std::io::Result<()> { 5 let mut data: Vec<Vec<&str>> = Vec::new(); 6 for line in BufReader::new(File::open("data.csv")?).lines() { 7 data.push(line?.split(',').collect::<Vec<_>>()); 8 } 9 10 //以下に行番号・列番号の入力を受け付けてデータを出力するコードを書く... 11 //... 12 //... 13 14 Ok(()) 15}

エラーメッセージ

error[E0597]: borrowed value does not live long enough --> Csv.rs:7:19 | 7 | data.push(line?.split(',').collect::<Vec<_>>()); | ^^^^^ - temporary value dropped here while still borrowed | | | temporary value does not live long enough ... 15 | } | - temporary value needs to live until here | = note: consider using a `let` binding to increase its lifetime error: aborting due to previous error For more information about this error, try `rustc --explain E0597`.

Boxを使ったりしてどうにか所有権の問題を回避しようとしましたが,自分の力量ではうまくできませんでした。
ファイル内のデータを一気に全部読みだした後に、読みだした文字列を適切に切り分けて2次元のベクタを生成する等の別の方法があるとは思いますが、
上記のプログラムのように一行ずつデータを読み込みながら,2次元のベクタを生成する方法を知りたいです.
以上よろしくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

こんにちは。

ひとまず動くようにする方法を紹介すると、data の型を Vec<Vec<String>>&str ではなく String を保持するようにし、残りのプログラムも適当に書き換えるとコンパイルが通るようになります。

rust

1use std::fs::File; 2use std::io::{BufRead, BufReader}; 3 4fn main() -> std::io::Result<()> { 5 let mut data: Vec<Vec<String>> = Vec::new(); 6 for line in BufReader::new(File::open("data.csv")?).lines() { 7 // `&str` から `String` に変換する 8 data.push(line?.split(',').map(String::from).collect::<Vec<_>>()); 9 } 10 11 //以下に行番号・列番号の入力を受け付けてデータを出力するコードを書く... 12 //... 13 //... 14 15 Ok(()) 16}

少し解説を加えます。
Rustの文字列にはデータを所有する String と、データ(の一部)を参照する &str があります。
今回は &str の参照とライフタイムの問題でした。

元のコードでは、まず for 内で1行読み、String 型の値 line を確保しています。

text

1for { 2 line: String 3 | 4 v 5 +---+---+---+---+---+ 6 | a | , | b | , | c | 7 +---+---+---+---+---+ 8}

そのあとに split でそのデータを参照した &str を作っていました。

text

1for { 2 line: String 3 | 4 v 5 +---+---+---+---+---+ 6 | a | , | b | , | c | 7 +---+---+---+---+---+ 8 ^ ^ ^ ^ ^ ^ 9 +---+ +---+ +---+ 10 | | | 11 tmp1 tmp2 tmp3 12 | | | 13 +-------+-------+ 14 | 15 splitで生成した参照 16}

これは、 line のデータを参照しているので line のライフタイムである for 内までしか生存しません。

text

1for { 2 line: String 3 | 4 v 5 +---+---+---+---+---+ 6 | a | , | b | , | c | 7 +---+---+---+---+---+ 8 ^ ^ ^ ^ ^ ^ 9 +---+ +---+ +---+ 10 | | | 11 tmp1 tmp2 tmp3 12 | | | 13 +-------+-------+ 14 | 15 splitで生成した参照 16 17 // <-- ここで終わり 18}

しかし、この参照を for 内より長生きする data に保管しようとするのでエラーになっています。

text

1data 2 3for { 4 line: String 5 | 6 v 7 +---+---+---+---+---+ 8 | a | , | b | , | c | 9 +---+---+---+---+---+ 10 ^ ^ ^ ^ ^ ^ 11 +---+ +---+ +---+ 12 | | | 13 tmp1 tmp2 tmp3 14 | | | 15 +-------+-------+ 16 | 17 splitで生成した参照 18 19 // <-- ここで終わり 20} 21 22 23// <- dataはmainの末尾まで生きる

なので参照をやめて、新たに String にデータを所有させるようにするのがこの提案です。

text

1for { 2 line: String 3 | 4 v 5 +---+---+---+---+---+ 6 | a | , | b | , | c | 7 +---+---+---+---+---+ 8 | | | 9 | | +---+ 10 | +---------+ | 11 +--------------+ | | 12 | | | 13 +---+ | | | 14tmp1: String ->| a |<+ | | 15 +---+ | | 16 +---+ | | データをコピー 17tmp2: String ->| b |<---+ | 18 +---+ | 19 +---+ | 20tmp3: String ->| c |<-----+ 21 +---+ 22 23 // このあと `data` にムーブする 24}

一見コピーが走って無駄なように見えますが、Pythonの方の実装も同じような挙動をしているはずです。
Rustの split が効率的すぎるだけです。

蛇足ですが、 line を長生きする別の場所に一旦退避してから split をとれば文字列データのコピーなく2次元ベクタを生成することもできます。
しかしこれは質問の内容に反しますね。

rust

1use std::fs::File; 2use std::io::{BufRead, BufReader}; 3 4fn main() -> std::io::Result<()> { 5 // `data` は `&str` に戻った 6 let mut data: Vec<Vec<&str>> = Vec::new(); 7 // データの一時退避場所を用意する 8 let mut preserve: Vec<String> = Vec::new(); 9 for line in BufReader::new(File::open("data.csv")?).lines() { 10 preserve.push(line?); 11 } 12 // // forの中身がシンプルになったので上記は以下でもよい 13 // let preserve = BufReader::new(File::open("data.csv")?) 14 // .lines() 15 // .collect::<std::io::Result<Vec<_>>>()?; 16 17 for line in &preserve { 18 // `preserve` のおかげでライフタイムが伸びて、エラーが出ない 19 data.push(line.split(',').collect::<Vec<_>>()); 20 } 21 //以下に行番号・列番号の入力を受け付けてデータを出力するコードを書く... 22 //... 23 //... 24 25 Ok(()) 26}

蛇足も含めて参考になれば幸いです。

投稿2020/05/24 18:16

blackenedgold

総合スコア468

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

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

sanagif

2020/05/25 12:17

ご丁寧な解説ありがとうございます。 とても参考になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問