2019/04/22

Rustの便利なiter::from_fn

イテレータの実装が容易になった

今までRustではIteratorトレイトを使うことで独自イテレータを実装していました。例えばstartとendというフィールドを持つMyRangeという構造体があるとします。この構造体にイテレータを実装する場合、以下のような方法で行っていました。


struct MyRange {start: i32, end: i32}

impl Iterator for MyRange {
    type Item = i32;
    fn next(&mut self) -> Option<i32> {
        if self.start >= self.end {
            return None;
        }
        let result = Some(self.start);
        self.start += 1;
        result
    }
}

fn main() {
    let mut myrange = MyRange {start: 0, end: 2};

    println!("{:?}", myrange.next());   // Some(0)
    println!("{:?}", myrange.next());   // Some(1)
    println!("{:?}", myrange.next());   // None
}

しかしfrom_fnを使うことで、より柔軟かつ簡潔にイテレータを実装することができるようです。上記のコードを変更してみます。

use std::iter::from_fn;

#[derive(Debug)]
struct MyRange {start: i32, end: i32}

fn main() {
    let mut myrange = MyRange {start: 0, end: 2};
    let mut itr = from_fn(|| {
        myrange.start += 1;

        if myrange.start <= myrange.end {
            Some(myrange.start)
        } else {
            None
        }
    });

    println!("{:?}", itr.next());   // Some(0)
    println!("{:?}", itr.next());   // Some(1)
    println!("{:?}", itr.next());   // None
}
同様の結果を得ることができました。つまり、必要な時、必要な形で利用できるイテレータを生成できるというわけです。例えば、イテレートする際のステップ数を1ではなく、2にしたい場合もあるでしょう。その時も、その場でちょいちょいと数字を変更すればいいだけなので、非常に柔軟性のある処理を行うことが可能です。今後うまく使えるようになっていきたいと思います。