Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

IntoIter

Rust では、コレクションは Iterator トレイトを使ってイテレートされます。これは Drop よりも少し複雑です。

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

ここで新しく登場したのは type Item です。これは、Iterator のすべての実装が Item という関連型を持つことを宣言しています。この場合、これは next を呼び出したときに吐き出せる型です。

Iterator が Option<Self::Item> を返す理由は、このインターフェイスが has_nextget_next の概念をまとめているからです。次の値がある場合は Some(value) を返し、ない場合は None を返します。これにより、API は一般に使いやすく安全に使用・実装できるようになり、同時に has_nextget_next の間の冗長なチェックやロジックを避けられます。いいですね!

残念ながら、Rust には yield 文のようなものは(まだ)ないので、そのロジックは自分で実装する必要があります。また、実際には各コレクションが実装するよう努めるべきイテレータが 3 種類あります。

  • IntoIter - T
  • IterMut - &mut T
  • Iter - &T

実のところ、List のインターフェイスを使って IntoIter を実装するための道具はすでにすべて揃っています。単に pop を何度も呼び出せばよいのです。そのため、IntoIter は List を包む newtype ラッパーとして実装するだけにします。

// タプル構造体は構造体の別形式で、
// 他の型の簡単なラッパーに便利です。
pub struct IntoIter<T>(List<T>);

impl<T> List<T> {
    pub fn into_iter(self) -> IntoIter<T> {
        IntoIter(self)
    }
}

impl<T> Iterator for IntoIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        // タプル構造体のフィールドには番号でアクセスする
        self.0.pop()
    }
}

そして、テストを書いてみましょう。

#[test]
fn into_iter() {
    let mut list = List::new();
    list.push(1); list.push(2); list.push(3);

    let mut iter = list.into_iter();
    assert_eq!(iter.next(), Some(3));
    assert_eq!(iter.next(), Some(2));
    assert_eq!(iter.next(), Some(1));
    assert_eq!(iter.next(), None);
}
> cargo test

     Running target/debug/lists-5c71138492ad4b4a

running 4 tests
test first::test::basics ... ok
test second::test::basics ... ok
test second::test::into_iter ... ok
test second::test::peek ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured

いいですね!