IntoIter

Rustでは要素の集まりをイテレートするときにはIteratorトレイトを使います. このトレイトはDropよりも若干複雑です:

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

ここでtype Itemというブロックが新しく出てきました.これはIteratorを実装する 型には関連付けられた型があることを表しています.この場合nextしたときに 出てくる型のことです.

nextがOption<Self::Item>を返す理由は,このメソッドがhas_nextget_nextの 役割を併せ持つからです.もし次の値があればSome(value)を返し,なければNoneを 返すわけです.これによって,より人間工学的で安全なAPIを構築しつつhas_nextget_nextを別々に実装した際の冗長さを回避できるのです.うまい手ですね!

悲しいことにRustには(まだ)yield文のようなものはありません.なのでそれに 相当する処理を自力で実装する必要があります.さらに,実はイテレータには 3つの種類があり,それぞれを実装しなくてはいけません:

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

実はIntoIterを実装するために必要なものはすでに揃っています.ただpopを 呼びまくればいいだけです.IntoIterをListのラッパーとして,こんな感じに 実装できます:

// タプル構造体はstructの変化形の一つです
// 他の型のラッパーを作るときに便利
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

いい感じですね!