【Rust入門】イテレータ(Iterator)の基本を分かりやすく解説

Rust におけるイテレータ(Iterator)の基本を初心者にも分かりやすく解説します。
イテレータとは
イテレータ(Iterator)は、値の列を生成する値で、要素を順番に取り出すことができる仕組みです。Rust では、文字列や Vec などから要素を取り出して処理するイテレータが用意されています。他にも、様々な場面でイテレータの仕組みが利用されています。
Rust の for 文はイテレータにとって自然に扱える構文となっており、シンプルなプログラム例は以下のようなものです。
fn main() {
let v = vec![1, 2, 3];
for x in v {
println!("{}", x);
}
}【実行結果】 1 2 3
Rust では、このような繰り返し処理の多くがイテレータを使って実装されており、Rust における重要な仕組みです。Rust のイテレータは、具体的には、Iterator トレイトや IntoIterator トレイトにより表現されています。
この記事では、Rust のイテレータ(Iterator)の基本を解説します。
イテレータの基本
イテレータを実現する中心的なトレイトは、Iterator トレイトと IntoIterator トレイトです。ここでは、それぞれの概要について紹介します。
Iterator トレイト
Iterator トレイトは、以下のように定義されています。
pub trait Iterator {
type Item;
// Required method
fn next(&mut self) -> Option<Self::Item>;
...(他のメソッドは省略)...
}Item という取り出す要素の型と next メソッドを持ちます。独自の型に Iterator トレイトを実装する際には next メソッドは必須のメソッドになります。他にもトレイトで定義されているメソッドはありますが、明確に定義しない場合はデフォルト実装が使用されます。
next メソッド
next メソッドは、イテレータの中心となる重要なメソッドです。
fn main() {
let v = vec![1, 2, 3];
let mut iter = v.iter();
println!("{:?}", iter.next()); // Some(1)
println!("{:?}", iter.next()); // Some(2)
println!("{:?}", iter.next()); // Some(3)
println!("{:?}", iter.next()); // None
}【実行結果】 Some(1) Some(2) Some(3) None
iter メソッドは、対象のイテレータを取得します。(iter については後ほど説明します。)
イテレータにおける next メソッドは、順に要素を取り出し、値 x がある場合は、Option 型の Some(x) を返却します。要素を取り出し続け、取り出す要素がなくなったら最後に None を返却します。 イテレータは、Option 型を使って処理の終了を表現していることがポイントです。
v は、イミュータブル(変更不可)ですが、iter は mut を付けてミュータブル(変更可能)にしています。これは、next() メソッドがイテレータ自身の状態を更新するためであり、イテレータは mut で宣言する必要があります。
IntoIterator トレイト
IntoIterator トレイトは、イテレータを生成できる型を表すトレイトで、以下のように定義されています。
pub trait IntoIterator {
type Item;
type IntoIter: Iterator<Item = Self::Item>;
// Required method
fn into_iter(self) -> Self::IntoIter;
}IntoIterator を実装している型は、into_iter メソッドによってイテレータに変換することができます。
into_iter メソッド
into_iter メソッドは、値や参照からイテレータを生成するメソッドです。into_iter() で理解しておくべきことは以下の通りです。対象 v に対してそれぞれ呼ばれる実装が変わります。
(&v).into_iter():不変参照のイテレータを生成する(&mut v).into_iter():可変参照のイテレータを生成するv.into_iter():所有権を移動するイテレータを生成する
以下の例を見てみましょう。
fn main() {
let v1 = vec![1, 2, 3];
// 不変参照のイテレータを作成
for x in (&v1).into_iter() {
println!("{}", x);
}
let mut v2 = vec![4, 5, 6];
// 可変参照のイテレータを作成
for x in (&mut v2).into_iter() {
*x += 1; // 各要素に1を加える
println!("{}", x);
}
let v3 = vec![7, 8, 9];
// 所有権を移動するイテレータを作成
for x in v3.into_iter() {
println!("{}", x);
}
// v3は所有権が移動したため、ここで使用できない
// println!("{:?}", v3); // コンパイルエラー
}【実行結果】 1 2 3 5 6 7 7 8 9
このように、into_iter() は同じ名前のメソッドであっても、値に対して呼ぶのか、参照に対して呼ぶのかで返すイテレータの型が異なります。この違いは、IntoIterator が「値」「不変参照」「可変参照」のそれぞれに対して実装されていることによるものです。そのため、要素の型も T / &T / &mut T のように変化します。なお、T は型パラメータです。
for 文と Iterator の関係
Rust の for 文は、IntoIterator トレイトを実装している型に対して使用できます。つまり、for 文は内部的に into_iter() を呼び出し、イテレータとして要素を取り出しています。
for 文で繰り返し処理を実装する場合は、明示的に into_iter() を書かなくても、以下のように不変参照で繰り返し処理を行うことができます。なお、可変参照や所有権移動の場合も同様に考えることができます。
for x in &v {
println!("{}", x);
}これは、内部的には、概ね次のような処理が行われていると考えることができます。
fn main() {
let v = vec![1, 2, 3];
let mut iter = (&v).into_iter();
while let Some(x) = iter.next() {
println!("{}", x);
}
}つまり、「for x in &v」は、(&v).into_iter() を使ってイテレータを生成し、next() で要素を順番に取り出して処理をしているというイメージです。
コレクションにおける iter や iter_mut メソッド
Vec などのコレクションでは、Iterator を返すメソッドとして、iter メソッドや iter_mut メソッドが提供されています。名称の通り、iter メソッドは不変参照のイテレータを、iter_mut メソッドは、可変参照のイテレータを返却します。
以下の例で見てみましょう。
fn main() {
let mut v = vec![1, 2, 3];
// iter メソッドで不変参照のイテレータを返却
for x in v.iter() {
println!("{}", x);
// 不変参照なので、以下のように値を変更することはできない
// *x += 1;
}
// iter_mut メソッドで可変参照のイテレータを返却
for x in v.iter_mut() {
*x += 1;
println!("{}", x);
}
}【実行結果】 1 2 3 2 3 4
例えば、iter を使用すると要素への不変参照を順番に取り出すことができます。不変参照なので、値を変更しようとするとコンパイルエラーとなります。一方で、iter_mut を使用すると要素への可変参照を取得できるため、要素の値を書き換えることができます。
ポイント
多くのコレクションでは以下のような対応関係があります。
v.iter()は(&v).into_iter()に対応v.iter_mut()は(&mut v).into_iter()に対応
つまり、iter や iter_mut は、参照に対する into_iter() をより分かりやすく使うためのメソッドと考えることができます。ただし、誤解しやすいポイントですが、iter や iter_mut は、Iterator トレイトや IntoIterator トレイトで定義されているメソッドではありません。
これらは、Rust の標準ライブラリにおいて、コレクションがイテレータを生成するための便利なメソッドとして提供しているものです。そのため、必ずしも上記対応が保証されているわけではありませんので注意してください。
Iterator トレイトの必須メソッドとされている next や IntoIterator トレイトの必須メソッドとされている into_iter とは位置づけが異なることを理解しておきましょう。
まとめ
Rust におけるイテレータ(Iterator)の基本を解説しました。
イテレータは、値の列を生成する値で、要素を順番に取り出すことができる仕組みで for 文などの繰り返し処理で利用されます。
イテレータの中心となるのが、Iterator トレイトと IntoIterator トレイトです。この記事では、それぞれの必須メソッドである next や into_iter メソッドの役割について説明しました。また、for 文との関係や、Rust のコレクションで提供される iter、iter_mut といったメソッドについても紹介しました。
イテレータは、Rust のデータ処理の中心的な仕組みです。基本の考え方をしっかりと理解しておきましょう。
上記で紹介しているソースコードについては GitHub にて公開しています。参考にしていただければと思います。







