Rustのfor文の書き方を解説します【for in, iter】
目次
Rustのfor文の書き方
for文はループ文の基本ですがRustをマスターするにあたって避けては通れない文です。
Rustのfor文には大きく分けて0..10
などで回す方法とベクタなどをiter()
で呼び出して回す方法があります。
この記事ではこれらの方法について具体的に解説していきます。
結論から言うと↓の3つが代表的なfor文になります。
for i in 0..4 { println!("{}", i); }
let v = vec!["aaa", "bbb", "ccc"]; for el in v.iter() { println!("{}", el); }
let v = vec!["aaa", "bbb", "ccc"]; for el in v { println!("{}", el); }
Rangeを使ったfor文
for文ではfor オブジェクト in イテレータ
を使ってループを回します。
in
の左側にはカウント変数などのオブジェクト、in
の右側にはイテレータなどが指定されます。
最も基本的な形のfor文が↓です。
for i in 0..4 { println!("{}", i); }
↑のコードを実行すると↓になります。
0 1 2 3
0..4
という指定は「0
から4
より下まで」値を生成するという指定です。
これはイテレータが生成されます。
この指定ではカウント変数i
に0
, 1
, 2
, 3
という順で整数が渡されていきます。
カウント変数はベクタのインデックス(添え字)などで使えます。
C/C++系の言語になれているとなかなか目新しい記法かもしれません。
カウント変数を逆順に回す
先ほどのfor文は0
から4
より下までカウント変数を増加させました。
これの逆につまり大きい値から小さい値へカウント変数を減らしていきたい場合もあります。
そういう場合はrev()
メソッドを使います。
for i in (0..4).rev() { println!("{}", i); }
↑のコードを実行すると↓になります。
3 2 1 0
for in
系のfor文でありがちなのですがカウント変数を逆に回したい時はちょっとめんどくさい記法が必要になることが多いです。
Rustもご多分に漏れずそんな感じですね。
n..=mを使って回す
0..4
は「0
から4
より下まで」ですが0..=4
にすると「0
から4
まで回す」になります。
for i in 0..=4 { println!("{}", i); }
↑のコードを実行すると↓になります。
0 1 2 3 4
m
(4
)を範囲内に含めたい場合にはこの指定が使えます。
カウント変数でベクタを参照する
ベクタのメソッドlen()
は要素数を返します。
これとn..m
を組み合わせるとベクタの要素数の範囲内のカウント変数を得ることができます。
let v = vec![1, 2, 3, 4]; for i in 0..v.len() { println!("{}", v[i]); }
↑のコードを実行すると↓の結果になります。
1 2 3 4
特に注意しなくても0 .. v.len()
で範囲内の添え字になってくれる当たりナイスですよね。
これはn..m
がm
の1つ下までの添え字を生成するという仕様による恩恵と言えます。
カウント変数で配列を参照する
配列もlen()
メソッドを持っています。
ですのでベクタと同様の方法でカウント変数を取り出すことができます。
let ary = [1, 2, 3, 4]; for i in 0..ary.len() { println!("{}", ary[i]); }
iter()系でベクタと配列を回す
ベクタや配列はiter()
メソッドを使うことができます。
これはイテレータを生成するメソッドです。
これを使うとfor文でカウント変数を使わずにベクタや配列を回せるようになります。
イテレータで要素を直接取り出してfor文で回す場合に気になるのが要素の型です。
これは使うiter()
メソッド(iter()
系のメソッドは色々あります)によって変わります。
これを手っ取り早く確認するにはmatch
文を使うといいです。
iter()
系のメソッドは一覧にすると↓があります。
iter()
into_iter()
iter_mut()
ベクタのiter()でfor文を回す
ベクタのiter()
でfor文を回します。
let v = vec!["aaa", "bbb", "ccc"]; for el in v.iter() { match el { &"aaa" => println!("A {}", el), &"bbb" => println!("B {}", el), &_ => println!("C {}", el), } }
結果は↓になります。
A aaa B bbb C ccc
このiter()
は要素を借用で取得します。
ですので元のベクタが変形するということはありません。
借用は他の言語風に言うと参照みたいなものです。
配列のiter()でfor文を回す
配列も同様です。
let ary = ["aaa", "bbb", "ccc"]; for el in ary.iter() { match el { &"aaa" => println!("A {}", el), &"bbb" => println!("B {}", el), &_ => println!("C {}", el), } }
これの実行結果はさっきのベクタと同じです。
ベクタのinto_iter()でfor文を回す
into_iter()
はベクタをムーブさせます。
つまりこれを呼び出してfor文でベクタを回すとそのベクタを入れてあった変数はそれ以降使えなくなります。
let v = vec!["aaa", "bbb", "ccc"]; for el in v.into_iter() { match el { "aaa" => println!("A {}", el), "bbb" => println!("B {}", el), _ => println!("C {}", el), } } // println!("{}", v.len()); // error! value borrowed here after move
↑のコードを実行すると↓になります。
A aaa B bbb C ccc
ムーブセマンティクスを使ったfor文ということでRustの神髄を見ることができる機能と言えます。
配列のinto_iter()でfor文を回す
ただ配列の場合のinto_iter()
はちょっとベクタと違っています。
let ary = ["aaa", "bbb", "ccc"]; for el in ary.into_iter() { match el { "aaa" => println!("A {}", el), "bbb" => println!("B {}", el), _ => println!("C {}", el), } } println!("{}", ary.len()); // 4 println!("{}", ary[0]); // aaa println!("{}", ary[1]); // bbb println!("{}", ary[2]); // ccc
なんと配列のほうのinto_iter()
は配列がムーブされません。
ですのでループが終わっても参照可能です。
なぜベクタと配列でこのような違いが出るのか?
というところですが、これは現在調査中です。
(^ _ ^) | Rustの7不思議さ |
(・ v ・) | 不思議な不思議なRustさん |
ベクタのiter_mut()でfor文を回す
ベクタをfor文で回すときにfor文の中で要素の値を変更したい、という場合があります。
そういう時はiter_mut()
を使います。
let mut v = vec!["aaa", "bbb", "ccc"]; for el in v.iter_mut() { *el = match el { &mut "aaa" => "AAA", &mut "bbb" => "BBB", _ => "CCC", } } println!("{}", v[0]); // AAA println!("{}", v[1]); // BBB println!("{}", v[2]); // CCC
↑のコードを実行すると↓になります。
A aaa B bbb C ccc aaa ddd bbb ddd ccc ddd
このiter_mut()
を使う時は元のオブジェクト、つまりベクタにmut
が付いてないといけません。
ベクタを破壊的に変更するのでmut
が必要です。
上記の例ではベクタ内の各要素を新しいstr
で変更しています。
match
文を見ると&mut "aaa"
となっているのがわかります。
配列のiter_mut()でfor文を回す
配列もiter_mut()
で回せます。
let mut ary = ["aaa", "bbb", "ccc"]; for el in ary.iter_mut() { *el = match el { &mut "aaa" => "AAA", &mut "bbb" => "BBB", _ => "CCC", } } println!("{}", ary[0]); // AAA println!("{}", ary[1]); // BBB println!("{}", ary[2]); // CCC
実行結果は先ほどのベクタと同じになります。
Rustのオブジェクトは基本的にイミュータブル(変更不可)なので、変更しようと思うとこのように専用のメソッドを使ったりmut
キーワードを付ける必要があります。
カウント変数やiter()系メソッドを使わない方法
カウント変数もiter()
系のメソッドもタイプ数という面ではコストがかかります。
もっともコストがかからないfor文をここから紹介します。
カウント変数やiter()を使わずにベクタや配列を回す
実はベクタはそのままfor文に渡すとinto_iter()
と同じ挙動をします。
let v = vec!["aaa", "bbb", "ccc"]; for el in v { match el { "aaa" => println!("AAA"), "bbb" => println!("BBB"), _ => println!("CCC"), } } // println!("{}", v.len()); // error! value borrowed here after move
↑のコードを実行すると↓になります。
AAA BBB CCC
Rustはデフォルトでムーブするという仕様を思い出してください。
これはfor文とベクタの場合でも同様です。
iter()を使わずに配列をfor文で回す
配列も同様にinto_iter()
と同じ挙動をします。
let ary = ["aaa", "bbb", "ccc"]; for el in ary { match el { "aaa" => println!("AAA"), "bbb" => println!("BBB"), _ => println!("CCC"), } } println!("{}", ary.len()); // 3
おわりに
今回はRustのfor文について解説しました。
for文をマスターして大量処理ができるようになりましょう。
(^ _ ^) | ループは |
(・ v ・) | 力 |