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

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

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

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

Q&A

解決済

3回答

4405閲覧

rustのエラーメッセージの意味がわかりません

momijiMac

総合スコア39

Rust

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

0グッド

1クリップ

投稿2018/08/19 23:54

前提・実現したいこと

rustのエラーメッセージの意味がわかりません。

シェルから以下のように引数を評価して、+1して表示させる
プログラムです。

cargo run 123
124

なにが問題なのでしょうか?

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

error[E0507]: cannot move out of indexed content --> src/main.rs:12:24 | 12 | println!("{}",add1(arg[1])); | ^^^^^^ cannot move out of indexed content

該当のソースコード

rust

1 2use std::env; 3 4fn main() { 5 let arg: Vec<String> = env::args().collect(); 6 println!("{}",add1(arg[1])); 7 8} 9 10fn add1(s: String) -> i32{ 11 let a:i32 = s.parse().unwrap(); 12 return a + 1 13} 14 15 16#[test] 17fn test_add1(){ 18 let x = "5".to_string(); 19 assert_eq!(add1(x), 6); 20} 21 22 23

試したこと

ソースコードのテストはパスする

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

rustc 1.24.1 (d3ae9a9e0 2018-02-27)
cargo 0.25.0 (96d8071da 2018-02-26)

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

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

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

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

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

guest

回答3

0

すでにベストアンサーが選ばれていますが、補足させてください。(他の回答に気づかずに回答を途中まで書いてしまった)

エラーとなる理由は他の方が説明されているとおり所有権によるものです。Vecから1要素だけmove out(所有権を移動)させることはできません。

clone()は解決方法のひとつですが、元の値(ここではString)を複製しますので、ある程度の実行時のコストがかかります。可能な限りはベストアンサーの2つ目の方法である借用を使うのがおすすめです。

rust

1fn main() { 2 // Vec<String>型 3 let v = vec!["zero".to_string(), "one".to_string()]; 4 // Vecから1要素だけmove out(所有権を移動)させることはできない。 5 // 質問と同じコンパイルエラー(cannot move out of indexed context)になる 6 // let v1 = v[1]; // String型 7 8 // `&`を使って値を借用すれば、値を複製しないで済む(おすすめの方法) 9 let v1_ref = &v[1]; // &String型 10}

借用を使う場合はプログラムを以下のように修正します。

rust

1use std::env; 2 3fn main() { 4 // Vec<String>型 5 let arg = env::args().collect::<Vec<_>>(); 6 // &arg[1]は&String型 7 println!("{}", add1(&arg[1])); 8} 9 10// 引数を&str型にする。こうするとコンパイラの型強制というしくみを 11// 使って、引数として&str型の値と、&String型の値の両方を取れるように 12// なるので便利 13fn add1(s: &str) -> i32 { 14 let a = s.parse::<i32>().unwrap(); 15 a + 1 // 最後の式の値が返されるのでreturnキーワードは不要 16} 17 18#[test] 19fn test_add1() { 20 let x = "5".to_string(); // String型 21 assert_eq!(add1(&x), 6); 22 let x = "7"; // &str型 23 assert_eq!(add1(x), 8); 24}

その他の方法としてはstd::mem::replace()を使って、今の値はmove outし、元の場所に代わりの値を入れることもできます。

rust

1fn main() { 2 // ベクタの内容を変更するので`mut`が必要 3 let mut v = vec!["zero".to_string(), "one".to_string()]; 4 5 // 今の値をmove outし、元の場所に代わりの値(例:"dummy")を入れる 6 // v1はString型 7 let v1 = std::mem::replace(&mut v[1], "dummy".to_string()); 8}

ところでコマンドライン引数を取り出すときは、また別の回答にある nth() を使うのが一般的です。これにコマンドライン引数のエラー処理なども加えると元のプログラムは以下のようになります。

rust

1use std::env; 2use std::error::Error; 3 4// 注意:main関数からResult型を返すにはRust 1.26.0かそれ以降が必要 5fn main() -> Result<(), Box<Error>> { 6 // String型 7 let arg1 = env::args().nth(1).ok_or("Please specify arg1.")?; 8 // &String → &strへ型強制する 9 println!("{}", add1(&arg1)?); 10 Ok(()) 11} 12 13fn add1(s: &str) -> Result<i32, Box<Error>> { 14 let a = s.parse::<i32>()?; 15 Ok(a + 1) 16} 17 18#[test] 19fn test_add1() { 20 let x = "5".to_string(); // String型 21 assert_eq!(add1(&x).unwrap(), 6); 22 let x = "7"; // &str型 23 assert_eq!(add1(x).unwrap(), 8); 24 25 // i32に変換できない文字列を与えるとエラー 26 assert!(add1("abc").is_err()); 27}

実行結果

# 正しいコマンドライン引数を与えた $ cargo run -- 123 124 # 引数を与えなかった $ cargo run -- Error: StringError("Please specify arg1.") # 数値に変換できない値を与えた $ cargo run -- abc Error: ParseIntError { kind: InvalidDigit }

投稿2018/08/20 02:46

編集2018/08/20 04:09
tatsuya6502

総合スコア2035

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

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

0

Rustの[1]はコピーではなく、移動のようです。
つまりargs[1]の要素を削除して、変数argに代入するということになります。
イミュータブルな配列の要素を削除しようとしているから怒られているのではないでしょうか。

Rust

1let arg1 = env::args().nth(1) 2println!("{}",add1(arg1));

とか

Rust

1let arg: Vec<String> = env::args().collect(); 2let ref arg1 = &arg[1]; 3println!("{}",add1(arg1));

とか
mutをつけるとミュータブルにできるようですので

Rust

1let mut arg: Vec<String> = env::args().collect(); 2println!("{}",add1(arg[0]));

とかではどうでしょうか?
ただし、デフォルトがイミュータブルのプログラミング言語で、
ミュータブルを使用するのは、それなりの理由がなければ忌み嫌われると思います。

投稿2018/08/20 01:18

編集2018/08/20 03:26
root_jp

総合スコア4666

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

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

Eki

2018/08/20 09:29

2つ目の例では、 arg1 の型は &&String となってしまい、型が合わずにコンパイルが通りません。また、これはミュータビリティの問題ではないので3つ目の例のように mut をつけてもコンパイルは通りません。 この仕様は少し不便ですが Vec の一部の要素がムーブされているか否か、コンパイラが静的に知ることはできないので仕方ないですね...。この例ではインデックスが定数ですが、もちろん実行時に決まる値にすることもできますしね。
root_jp

2018/08/20 11:12

ご丁寧に補足いただきありがとうございます。勉強になります。 あまり良くしらない言語に関して回答するものではありませんね。 失礼いたしました。
guest

0

ベストアンサー

所有権の問題ですね。
関数呼び出しはデフォルトで所有権を移動してしまい、配列の中身の所有権を移動することはできないのでエラーが起きています。
配列の中身を直接渡さず、cloneを渡すといいと思います。

Rust

1fn main() { 2 let arg: Vec<String> = env::args().collect(); 3 println!("{}",add1(arg[1].clone())); 4}

借用することで移動しないようにする方法もあります。

Rust

1use std::env; 2 3fn main() { 4 let arg: Vec<String> = env::args().collect(); 5 println!("{}",add1(&arg[1])); 6} 7 8fn add1(s: &String) -> i32{ 9 let a:i32 = s.parse().unwrap(); 10 return a + 1 11} 12 13 14#[test] 15fn test_add1(){ 16 let x = "5".to_string(); 17 assert_eq!(add1(&x), 6); 18}

投稿2018/08/20 01:10

de9

総合スコア312

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問