質問: ``` // Code 1 fn main(){ for vect in vec![0,1,2,3,4,5,6,7,8,9].iter(){ println!("{}",vect); } } ``` 例えば上記のコードを実行すると、縦に整列した10個の数字を得ることができます。同時に、 ``` // Code 2 fn main(){ for vect in vec![0,1,2,3,4,5,6,7,8,9]{ println!("{}",vect); } } ``` でも同じ結果を得ることができます。ここで本題ですが、`Code 1`と`Code 2`では何が異なるのでしょうか? またRustにおけるイテレータの存在意義とは? 自分の古巣のRubyにもイテレーターはありましたが、全て`for`で解決していたため、結局何のために(目標のデータを反復することには間違いない)用意されているのか疑問に思っていました。参考文献を読んでも`イテレーターの存在意義を前もって知っている事`を前提に書かれおり非常に悩んでいます。
回答: こんにちは。ちょっと長くなりますが、お付き合い下さい。 Code 1とCode 2の違い ================ まずは本題にお答えすると、Code 1とCode2では所有権の扱いが異なります。後程説明しますが、Code 1もCode 2も(内部的に)イテレータを使ったコードになっているので、イテレータを使うかどうかという違いはないです。 Code 1では `vec!` で作られた値を参照しており、 Code 2では `vec!` で作られた値を消費しています。これは1. `vect` の型が `&i32` か `i32` か、 2. `for` 式文のあとでベクトルが使えるかどうか、で違いが表われます。少しコードを書き換えて試してみましょう。 ``` // Code 1 fn main(){ // 一旦変数を束縛しておく let vec = vec![0,1,2,3,4,5,6,7,8,9]; for vect in vec.iter() { // vecを参照しているので、要素も参照になる // vectは&i32 let vect: &i32 = vect; println!("{}",vect); } // forのあともvecを使える println!("{:?}", vec); } ``` ``` // Code 2 fn main(){ // 一旦変数を束縛しておく let vec = vec![0,1,2,3,4,5,6,7,8,9]; for vect in vec { // vecを消費しているので、要素をそのまま取り出せる // vectはi32 let vect: i32 = vect; println!("{}",vect); } // forのあとvecを使おうとすると所有権エラー // println!("{:?}", vec); // error[E0382]: borrow of moved value: `vec` // --> iter.rs:8:22 // | // 2 | let vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; // | --- move occurs because `vec` has type `std::vec::Vec`, which does not implement the `Copy` trait // 3 | for vect in vec { // | --- // | | // | value moved here // | help: consider borrowing to avoid moving into the for loop: `&vec` // ... // 8 | println!("{:?}", vec); // | ^^^ value borrowed here after move // // error: aborting due to previous error // // For more information about this error, try `rustc --explain E0382` } ``` イテレータの存在意義 ========== イテレータと `for` の関係 次にイテレータの存在意義ですが、一番分かりやすいのは `for` 式文のためです。`for` とイテレータは独立したものではなく、 `for` はイテレータを便利に使うための構文なのです。 Rustで `for` を書いたときは、コンパイラが裏で `while let` という構文とイテレータを使った表現に書き換えています。 ``` // for for e in v { // do something } ``` ``` // 上の式はこう展開される let mut iter = v.into_iter(); while let Some(e) = iter.next() { // do something } ``` Rubyも同様に `for` 文は `each` で書き換えられます(参考: 制御構造 (Ruby 2.7.0 リファレンスマニュアル)) RubyでもRustでも配列や範囲(`start..end`)など、違うオブジェクトを `for` で扱えるのはイテレータで抽象化しているからなのです。 その他の存在意義 イテレータの他の存在意義は 1. 「くりかえせるもの」という概念を抽象化する 2. `for` でやりがちな処理をメソッドとして共通化できる 3. (Rustにおいては)パフォーマンス上のメリットもある などが挙げられます。3は少し込み入るので1, 2についてだけ触れます。 ### 1.「くりかえせるもの」という概念を抽象化するについて Rubyだとダックタイピングがあるので意義が薄いですが、Rustだと概念の抽象化は必須です。例えばベクトルを拡張する `extend` メソッドはこういう型シグネチャをしています。 ``` fn extend(&mut self, iter: I) where I: IntoIterator, ``` イテレート可能なもの(正確には `IntoIterator` を実装している型ですが)ならなんでも引数にとれます。このおかげで、次のようにベクトルやスライス、範囲などさまざまなものを引数にとれます。 ``` let mut vec = vec![1]; // Vec を渡せる vec.extend(vec![2, 3]); // &[T] を渡せる vec.extend([4, 5, 6]); // Range を渡せる vec.extend(7..10); ``` このように、「くりかえせるもの」を値として扱いたいときに有用です。 ### 2.`for` でやりがちな処理をメソッドとして共通化できるについて そのままですが、よくある処理をメソッドとして共通化できると便利です。例えば「最初の5要素の和をとる」処理はイテレータを使えばRustでこう書けます。 ``` vec.into_iter().take(5).sum() ``` 同じことは `for` でも書けますが、 5つ数えたり合計値を収める変数を用意したり、ちょっと手間がかかりますよね?それらをメソッドにまとめることで手間を節約できます。あるいは、 `zip`のように `for` では少し書きづらいような処理もメソッドにできます。 まとめ === 以上で `vec![]` と `vec![].iter()` の違いと、イテレータの存在意義が伝わったでしょうか。蛇足になりますが、下に余談が2つ続きます。 余談 == 余談1 `for vect in vec![..].iter() { /* ... */}` について 先程説明したとおり、この式は以下のように展開されます。 ``` let mut vec = vec![..].iter().into_iter(); while let Some(vect) = vec.next() { // ... } ``` `vec![..].iter().into_iter()` という式が出てきましたね。イテレータを2回とっているようで無駄に見えるのは置いておきましょう。 注目するのは返り値です。この式は `std::slice::Iter` を返します。ところで、 `Vec` から `std::slice::Iter` を得るのは実は別のやり方があります。`(&vec![..]).into_iter()` でも同じものが出てくるのです。 つまり、 `vec![..].iter()` == `vec![..].iter().into_iter()` == `(&vec![..]).into_iter()` です。`into_iter` は `for` が勝手につけてくれるので、結局以下の2つの式は同等ということになります。 ``` // .iter() を呼ぶやりかた for vect in vec![..].iter() { // ... } ``` ``` // &をつけるやりかた for vect in &vec![..] { // ... } ``` Rustではイテレータを意識しない `&vec!..]` の方が好ましい書き方とされています。See [explicit_iter_loop 余談2 内部イテレータと外部イテレータ RubyのイテレータとRustのイテレータは実は種類が違います。Rubyのものは内部イテレータ、Rustのものは外部イテレータと呼ばれます。 Rubyのものは `it.each {|e| ... }` のように、処理をメソッドに渡してしまうスタイルです。これはイテレータ自身が繰り返し処理を担当します。 Rustのものは `while let Some(e) = it.next() { ... }` のように、他の制御構造と組合せて使うスタイルです。 今回の質問にこれらの違いが関係することはないのですが、両者をないまぜにしてイテレータのことを考えていると混乱しかねないので一言補足しました。