Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rustのイテレーター完全制覇 / domination-of-the-rust-iterators

optim
July 29, 2021

Rustのイテレーター完全制覇 / domination-of-the-rust-iterators

イテレーターの仕組みや使い方、そしてメニアックな話を紹介します。イテレーターを何となく敬遠している方、または雰囲気で使っている方、あるいはIteratorの知識をnextしたい方におすすめです。

OPTiM TECH NIGHT|Rustのイテレーター完全制覇! - connpass
https://optim.connpass.com/event/218495/

optim

July 29, 2021
Tweet

More Decks by optim

Other Decks in Programming

Transcript

  1. Rustのイテレーター完全制覇
    株式会社オプティム R&Dユニット 齋藤
    OPTiM TECH NIGHT OVER ZOOM

    2021/07/29
    Copyright © OPTiM Corp. All Rights Reserved. 1

    View Slide

  2. 齋藤( @aznhe21)
    HAL東京4年制卒 2017年度新卒入社 27歳
    最近の仕事:OPTiM AI CameraのAI周り (右下)
    VRゲームのBeat Saberを布教したい(右上)
    最近のTECH BLOGの投稿たち




    https://japanese.engadget.com/kddi-5-g-artificialintelligence-033658347.html
    2

    View Slide

  3. 目次
    1. イテレーター入門
    2. イテレーターの使い方
    3. イテレーターの作り方
    4. さいごに
    5. Q&A
    Copyright © OPTiM Corp. All Rights Reserved. 3

    View Slide

  4. イテレーター入門
    Copyright © OPTiM Corp. All Rights Reserved. 4

    View Slide

  5. 入門の前に:iterの読み方
    Rustではイテレーターを iter と略すことが多いです。
    Copyright © OPTiM Corp. All Rights Reserved. 5

    View Slide

  6. 入門の前に:iterの読み方
    Rustではイテレーターを iter と略すことが多いです。

    ここでは国際熱核融合実験炉に従って「イーター」と呼んでいます。
    https://ja.wikipedia.org/wiki/ITER
    Copyright © OPTiM Corp. All Rights Reserved. 6

    View Slide

  7. イテレーターのおさらい
    大雑把に言えばforループでのループ対象
    for i in 0..10 {

    // ^^^^^

    println!("{}", i);

    }

    let v = vec![1, 2, 3];

    for x in &v {

    // ^^

    println!("{}", i);

    }

    for entry in std::fs::read_dir(".")? {

    // ^^^^^^^^^^^^^^^^^^^^^^^

    let entry = entry?;

    println!("{:?}", entry.file_name());

    }

    Copyright © OPTiM Corp. All Rights Reserved. 7

    View Slide

  8. イテレーターのおさらい
    または関数型プログラミングスタイルで使うもの
    let v = vec![1, 2, 3];

    v.iter() // Iterator

    .copied() // Iterator

    .filter(|x| *x % 2 == 1) // Iterator

    .map(|x| x * 2) // Iterator

    .for_each(|x| println!("{}", x));

    // -> 2, 6

    イテレーターとは・・・
    値を1つずつ取り出して処理(=「反復」「イテレート(iterate)」)するためのもの
    日本語ではイテレーターのことを反復子と呼ぶ(JIS規格より)
    Copyright © OPTiM Corp. All Rights Reserved. 8

    View Slide

  9. イテレーターとして扱える型
    1. Range 系の型( 0..10 や 0.. など)
    2. スライスや Vec 、 HashMap などのコレクション型
    3. std::fs::read_dir() (の戻り値である std::fs::ReadDir 型)
    4. std::sync::mpsc::Receiver

    対応する std::sync::mpsc::Sender が生存している間、データを受信する度にイテレーターが進む
    5. etc...
    Copyright © OPTiM Corp. All Rights Reserved. 9

    View Slide

  10. Rustのイテレーターの特徴
    1. 回す時は手続き型(for文)でも関数型( for_each などでメソッドチェーン)でも使える
    読みやすい方で書ける
    2. 遅延評価
    JavaScriptだと
    const v = [1, 2, 3];

    v // -> Array

    .filter(x => x % 2 == 1) // -> Array

    .map(x => console.log(x)) // -> Array

    ;

    // 1, 3

    Rustだと
    let v = vec![1, 2, 3];

    v.iter() // -> Iterator

    .copied() // -> Iterator

    .filter(|x| *x % 2 == 1) // -> Iterator

    .map(|x| println!("{}", x)) // -> Iterator

    ;

    // 何も出力されない

    // warning: unused `Map` that must be used

    // = note: `#[warn(unused_must_use)]` on by default

    // = note: iterators are lazy and do nothing unless consumed

    Copyright © OPTiM Corp. All Rights Reserved. 10

    View Slide

  11. イテレーターは何者だ
    イテレーターとは Iterator トレイトを実装する型
    Range は Iterator を実装するが、 Vec は実装しない
    つまり Vec そのものはイテレーターではない
    pub trait Iterator {

    // ループする値の型

    type Item;

    // 次の値を取得する

    fn next(&mut self) -> Option;

    // ...

    }

    impl Iterator for ops::Range { /* ... */ }

    // VecへのIterator実装はない

    値が尽きるまで next() を呼び続けることで全ての値を列挙・反復できる。
    let mut iter = 1..3; // イテレーターは自身を変更するためmut

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

    println!("{:?}", iter.next()); // Some(2)

    println!("{:?}", iter.next()); // None

    Copyright © OPTiM Corp. All Rights Reserved. 11

    View Slide

  12. forループの正体
    forループはコンパイラによって IntoIterator::into_iter() を使ったwhileループに変換される。

    into_iter() は値をイテレーターに変換する。
    for x in &v {}

    ↑と↓は同義
    let mut iter = (&v).into_iter();

    // ^^^^

    while let Some(x) = iter.next() {}
    v.into_iter() と (&v).into_iter() は全く別の意味(使い方の章で説明)
    実際のループ対象は Vec 型の値ではなく IntoIterator::into_iter() の戻り値
    Vec 自体をループしているわけではない!
    Copyright © OPTiM Corp. All Rights Reserved. 12

    View Slide

  13. IntoIteratorを経由することの意義
    Iterator が直接実装されている Range の挙動を見てみると意義が分かりやすい。
    let mut range = 0..10;

    println!("{:?}", range); // 0..10

    range.next(); // イテレーターを進める

    println!("{:?}", range); // 1..10

    Range 自体がイテレーターのため Range そのものを変更してしまう
    Copyright © OPTiM Corp. All Rights Reserved. 13

    View Slide

  14. IntoIteratorを経由することの意義
    Vec 自体がイテレーターだった場合は Vec を書き換えることになってしまう。

    その場合は next() で先頭要素を削除することになり、2番目以降の要素を1つ前にずらすという処理が発生してしまう(図の左側)。
    代わりに別オブジェクトを用意し、追加でインデックスを保持することでコピーが無くなる(図の右側)。

    このオブジェクトに変換するのが IntoIterator::into_iter() の役割。
    Vec自体がIteratorの場合
    1 2 3
    next()
    2 3
    別にイテレーター型を用意する場合
    1 2 3
    next()
    1 2 3
    使い分けるなら
    値自身を変更しても良いなら直接 Iterator を実装
    値を変更したらまずい場合は IntoIterator を実装 ←基本こっち
    Copyright © OPTiM Corp. All Rights Reserved. 14

    View Slide

  15. IntoIteratorの定義
    pub trait IntoIterator {

    // Iteratorでループする値の型

    type Item;

    // 変換後のイテレーターの型

    type IntoIter: Iterator;

    // 値をイテレーターに変換する

    fn into_iter(self) -> Self::IntoIter;

    }

    Iterator には IntoIterator が自動で実装される。
    これによりイテレーター自体をforループの対象に出来る。
    impl IntoIterator for I {

    type Item = I::Item;

    type IntoIter = I;

    // イテレーターからはイテレーター自身を返す

    fn into_iter(self) -> I {

    self

    }

    }

    let v = vec![1, 2, 3];

    for x in v.iter() {

    println!("{}", x);

    }

    Copyright © OPTiM Corp. All Rights Reserved. 15

    View Slide

  16. イテレーターの使い方
    Copyright © OPTiM Corp. All Rights Reserved. 16

    View Slide

  17. 3パターンのイテレーター
    Vec を始め HashMap や BTreeMap などのコレクション型には IntoIterator が3パターン実装されており、

    使い方によって所有権の扱いが異なる。

    また、それぞれの IntoIterator と対応するメソッドも用意されており、関数型スタイルで使うことも出来る。
    Copyright © OPTiM Corp. All Rights Reserved. 17

    View Slide

  18. 3パターンのイテレーター:ムーブ
    値をムーブするので中身の所有権を奪うことが出来る。


    let v: Vec = vec![1, 2, 3];

    // xの型はu32

    for x in v { /* ... */ }

    for x in v.into_iter() { /* ... */ }

    // 関数型スタイル

    v.into_iter().for_each(|x| { /* ... */ });

    impl IntoIterator for Vec {

    type Item = T;

    type IntoIter = IntoIter;

    fn into_iter(self) -> IntoIter {

    /* ... */

    }

    }

    Copyright © OPTiM Corp. All Rights Reserved. 18

    View Slide

  19. 3パターンのイテレーター:借用
    借用するので値を再利用出来る。

    大抵の場合 IntoIterator は iter() を呼び出しているだけ。
    let v: Vec = vec![1, 2, 3];

    // xの型は&u32

    for x in &v { /* ... */ }

    for x in v.iter() { /* ... */ }

    // 関数型スタイル

    v.iter().for_each(|x| { /* ... */ });

    impl<'a, T, A: Allocator> IntoIterator for &'a Vec {

    type Item = &'a T;

    type IntoIter = slice::Iter<'a, T>;

    fn into_iter(self) -> slice::Iter<'a, T> {

    self.iter()

    }

    }

    Copyright © OPTiM Corp. All Rights Reserved. 19

    View Slide

  20. 3パターンのイテレーター:可変借用
    可変借用するので値を変更出来る。

    大抵の場合 IntoIterator は iter_mut() を呼び出しているだけ。
    let mut v: Vec = vec![1, 2, 3];

    // xの型は&mut u32

    for x in &mut v { /* ... */ }

    for x in v.iter_mut() { /* ... */ }

    // 関数型スタイル

    v.iter_mut().for_each(|x| { /* ... */ });

    impl<'a, T, A: Allocator> IntoIterator for &'a mut Vec {

    type Item = &'a mut T;

    type IntoIter = slice::IterMut<'a, T>;

    fn into_iter(self) -> slice::IterMut<'a, T> {

    self.iter_mut()

    }

    }

    Copyright © OPTiM Corp. All Rights Reserved. 20

    View Slide

  21. iter()の有無による違い
    良く疑問に思われがちなforループの .iter() の有無の違いはここまでくれば分かるはず。
    ムーブ
    for x in v {}

    for x in v.into_iter() {}

    借用
    for x in &v {}

    for x in (&v).into_iter() {}

    for x in v.iter() {}

    可変借用
    for x in &mut v {}

    for x in (&mut v).into_iter() {}

    for x in v.iter_mut() {}

    各セクション内のコードは同じ意味。
    Copyright © OPTiM Corp. All Rights Reserved. 21

    View Slide

  22. Iteratorトレイトのメソッド
    Iterator を関数型スタイルで使う時は map や find などを使うことになる。

    便利なメソッドは大量にあるので紹介記事は要チェック。
    このような Iterator のメソッドは随時追加されている。
    Iterator::copied :1.36で追加
    Iterator::reduce :1.51で追加
    今後追加が検討されている(安定化がまだな)メソッド
    Iterator::advance_by : next() を n 回呼ぶ
    Iterator::intersperse :末尾と要素の間に指定した値を追加する。

    collect() と組み合わせると join() 的な挙動になる
    Iterator::try_find :クロージャーから Ok(Some(v)) または Err が返ったらそれを返す
    etc...
    https://qiita.com/lo48576/items/34887794c146042aebf1
    Copyright © OPTiM Corp. All Rights Reserved. 22

    View Slide

  23. その他のイテレーター
    Iterator 以外のトレイトや Iterator のメソッドではない自由関数、

    イテレーターとして使うことを思いつかない型を紹介。
    Copyright © OPTiM Corp. All Rights Reserved. 23

    View Slide

  24. ExactSizeIterator
    pub trait Iterator {

    fn size_hint(&self) -> (usize, Option) { /* ... */ }

    }

    Iterator::size_hint はイテレーターがどれくらいループ出来るのかを示すメソッドで、

    「保証される最小の要素数」と「保証される最大の要素数」を返す。

    つまり、要素数が不明の場合は (0, None) を、要素数が1以上の場合は (1, None) を、

    要素数が5以上10以下の場合は (5, Some(10)) を返す。
    pub trait ExactSizeIterator: Iterator {

    fn len(&self) -> usize { /* ... */ }

    }

    ExactSizeIterator は正確な要素数が分かる場合に実装される。

    主にスライスや Vec 、 HashMap のイテレーターなどに実装される。
    size_hint との違いはまさしく要素数の保証があるかどうか。
    let mut iter = [0, 1, 2].iter();

    println!("{}", iter.len()); // 3

    iter.next();

    println!("{}", iter.len()); // 2

    Copyright © OPTiM Corp. All Rights Reserved. 24

    View Slide

  25. DoubleEndedIterator
    pub trait DoubleEndedIterator: Iterator {

    fn next_back(&mut self) -> Option;

    // ...

    }

    双方向イテレーターである場合に実装される。

    つまり最後の要素を1つ消費する(戻す)ことが出来るイテレーター( next() したあとに戻れるわけではない)。
    let mut iter = [0, 1, 2, 3].iter();

    println!("{:?}", iter.next_back()); // Some(3)

    println!("{:?}", iter.next_back()); // Some(2)

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

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

    println!("{:?}", iter.next_back()); // None

    追加メソッドとして、 nth_back / try_rfold / rfold / rfind などが使える。

    また Iterator::rev() も使えるようになる。
    Copyright © OPTiM Corp. All Rights Reserved. 25

    View Slide

  26. FusedIterator
    pub trait FusedIterator: Iterator {}

    イテレーターが None を返したら、その後もずっと None を返し続けることを保証するトレイト。
    実は Iterator::next() は None を返したあとに Some(v) を返しても良いと規定している
    イテレーターが None を返したあとでも None を返し続けることに依存するコードを書く場合、

    Iterator::fuse() を呼び出すことで None を返し続ける挙動にすることが出来る。

    ( FusedIterator に対して fuse を呼び出した場合でも呼び出しオーバーヘッドがないように実装されている)
    fn hoge>(mut iter: I) {

    // イテレーターを最後まで進める

    while let Some(_) = iter.next() {}

    println!("{:?}", iter.next()); // Noneである保証はない

    }

    fn fuga>(iter: I) {

    let mut iter = iter.fuse();

    while let Some(_) = iter.next() {}

    println!("{:?}", iter.next()); // 常にNone

    }

    Copyright © OPTiM Corp. All Rights Reserved. 26

    View Slide

  27. FromIterator
    pub trait FromIterator: Sized {

    fn from_iter>(iter: T) -> Self;

    }

    イテレーターを元に構築出来る型に実装される。
    これを実装することで Iterator::collect() が使えるようにな
    る。
    impl FromIteartor for X とすると、要素が I 型のイテレーターから X を構築出来る、という意味。
    Rustの標準ドキュメント(https://doc.rust-lang.org/std/iter/trait.FromIterator.html)を見ると

    実装されている型が大量にあることが分かる。
    1. FromIterator for String
    2. FromIterator> for Result
    要素が Result の場合に Result, E> で受け取るような形
    3. FromIterator> for Option
    要素が Option の場合に Option> で受け取るような形
    4. FromIterator> for PathBuf
    ["a", "b", "c"] から PathBuf("a/b/c") で受け取るような形
    Copyright © OPTiM Corp. All Rights Reserved. 27

    View Slide

  28. FromIterator
    Iterator から Vec を構築する
    let v: Vec = [1, 2, 3].iter().copied().collect();

    println!("{:?}", v); // [1, 2, 3]

    Iterator から String を構築する
    let v: String = ['a', 'b', 'c'].iter().copied().collect();

    println!("{:?}", v); // "abc"

    Iterator> から Result, u32> を構築する
    let v: Result, u32> = [Ok(1), Ok(2), Ok(3)].iter().copied().collect();

    println!("{:?}", v); // Ok([1, 2, 3])

    let v: Result, u32> = [Ok(1), Ok(2), Err(3)].iter().copied().collect();

    println!("{:?}", v); // Err(3)

    Iterator> から PathBuf を構築する
    let v: std::path::PathBuf = ["a", "b", "c"].iter().collect();

    println!("{}", v.display()); // "a/b/c"

    Copyright © OPTiM Corp. All Rights Reserved. 28

    View Slide

  29. std::iter::empty
    pub const fn empty() -> Iterator

    何も返さない、空のイテレーターを生成する。
    let mut iter = std::iter::empty::();

    assert_eq!(iter.next(), None);

    assert_eq!(iter.len(), 0); // ExactSizeIterator

    assert_eq!(iter.next_back(), None); // DoubleEndedIterator

    Copyright © OPTiM Corp. All Rights Reserved. 29

    View Slide

  30. std::iter::from_fn
    pub fn from_fn(f: F) -> Iterator

    where

    F: FnMut() -> Option

    指定されたクロージャーからイテレーターを生成する。

    Iterator::next がクロージャーとして実装されるイメージ。
    let iter = std::iter::from_fn(|| {

    match rand::random::() {

    i if i < 0 => None,

    i => Some(i),

    }

    });

    // 乱数が負の値になるまで値を出力する

    for x in iter {

    println!("{}", x);

    }

    Copyright © OPTiM Corp. All Rights Reserved. 30

    View Slide

  31. std::iter::once / once_with
    pub fn once(value: T) -> Iterator

    pub fn once_with(gen: F) -> Iterator

    where

    F: FnOnce() -> A

    指定された値(クロージャーから返された値)を1度返すイテレーターを生成する。
    // イテレーター生成時に乱数を得る

    let mut iter = std::iter::once(rand::random::());

    println!("{:?}", iter.next()); // Some(?)

    println!("{:?}", iter.next()); // None

    // イテレーター呼び出し時に乱数を得る

    let mut iter = std::iter::once_with(rand::random::);

    println!("{:?}", iter.next()); // Some(?)

    println!("{:?}", iter.next()); // None

    Copyright © OPTiM Corp. All Rights Reserved. 31

    View Slide

  32. std::iter::repeat / repeat_with
    pub fn repeat(elt: T) -> Iterator

    where

    T: Clone

    pub fn repeat_with(repeater: F) -> Iterator

    where

    F: FnMut() -> A

    指定された値(クロージャーを呼び出して得られる値)を無限に繰り返すイテレーターを生成する。
    let mut iter = std::iter::repeat(rand::random::());

    // どちらも同じ値

    println!("{:?}", iter.next());

    println!("{:?}", iter.next());

    let iter = std::iter::repeat_with(rand::random::)

    .take_while(|i| *i >= 0);

    // 乱数が負の値になるまで値を出力する

    for x in iter {

    println!("{}", x);

    }

    Copyright © OPTiM Corp. All Rights Reserved. 32

    View Slide

  33. std::iter::successors
    pub fn successors(first: Option, succ: F) -> Iterator

    where

    F: FnMut(&T) -> Option

    初期値からクロージャーを呼び出して値を計算し続けるイテレーターを生成する。
    雰囲気は Iterator::fold に近い。
    let mut iter = std::iter::successors(Some(1), |&v| {

    // bool.then()はtrueならSome(closure())を、falseならNoneを返す

    (v < 10).then(|| v * 10)

    });

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

    println!("{:?}", iter.next()); // Some(10)

    println!("{:?}", iter.next()); // None

    クロージャーから返った値は次の next() で返ることに注意。
    let mut iter = std::iter::successors(Some(0), |&x| {

    let x = x + 1;

    println!("closure: {}", x);

    Some(x)

    });

    println!("next: {}", iter.next().unwrap());

    // closure: 1

    // next: 0

    println!("next: {}", iter.next().unwrap());

    // closure: 2

    // next: 1

    Copyright © OPTiM Corp. All Rights Reserved. 33

    View Slide

  34. 意外なイテレーター
    Option と Result は IntoIterator を実装しているのでイテレーターとして扱える。
    let mut v = vec![0u32];

    v.extend(Some(1)); // 追加される

    v.extend(None::); // 追加されない

    v.extend(Ok::(2)); // 追加される

    v.extend(Err::(())); // 追加されない

    println!("{:?}", v); // [0, 1, 2]

    once と empty を条件で呼び変える代わりにも使える。
    fn get1() -> impl Iterator {

    if rand::random::() {

    Box::new(std::iter::once(rand::random::())) as Box>

    } else {

    Box::new(std::iter::empty()) as Box>

    }

    }

    fn get2() -> impl Iterator {

    if rand::random::() { Some(rand::random()) } else { None }.into_iter()

    }

    Copyright © OPTiM Corp. All Rights Reserved. 34

    View Slide

  35. イテレーターの作り方
    Copyright © OPTiM Corp. All Rights Reserved. 35

    View Slide

  36. Iteratorを実装する
    最低限実装しなければならないアイテム
    pub trait Iterator {

    type Item;

    fn next(&mut self) -> Option;

    }

    無限に値を返すイテレーターを作ってみる
    struct Hoge;

    impl Iterator for Hoge {

    type Item = u32;

    fn next(&mut self) -> Option {

    Some(1)

    }

    }

    fn main() {

    let mut iter = Hoge.map(|x| x * 2);

    println!("{:?}", iter.next()); // Some(2)

    println!("{:?}", iter.next()); // Some(2)

    println!("{:?}", iter.next()); // Some(2)

    }

    Copyright © OPTiM Corp. All Rights Reserved. 36

    View Slide

  37. カウントダウンするイテレーター
    struct CountDown(pub u32);

    impl Iterator for CountDown {

    type Item = u32;

    fn next(&mut self) -> Option {

    if self.0 == 0 {

    None

    } else {

    self.0 -= 1;

    Some(self.0)

    }

    }

    }

    fn main() {

    // 9 8 7 6 5 4 3 2 1 0

    for x in CountDown(10) {

    println!("{}", x);

    }

    }

    (0..N).rev() のイメージ。
    Copyright © OPTiM Corp. All Rights Reserved. 37

    View Slide

  38. 他に実装しておくべきもの
    pub trait Iterator {

    fn size_hint(&self) -> (usize, Option) { /* ... */ }

    }

    イテレーターの最小個数、または最大個数が分かる場合に実装する。
    pub trait ExactSizeIterator: Iterator {

    fn len(&self) -> usize { /* ... */ }

    }

    個数が特定出来る場合に実装する。 Iterator::size_hint と共に実装するべきである。
    len にデフォルト実装はあるものの、 size_hint を参照して保証が保たれているかをアサートするものであるため、自分で実装するべきである。
    pub trait DoubleEndedIterator: Iterator {

    fn next_back(&mut self) -> Option;

    }

    双方向イテレーターである場合に実装する。
    next_back は最後の要素をイテレーターから除去して返す。
    pub trait FusedIterator: Iterator {}

    イテレーターが None を返したあと、ずっと None を返し続ける場合に実装する。
    マーカートレイトであり、実装すべきメソッドはない。
    Copyright © OPTiM Corp. All Rights Reserved. 38

    View Slide

  39. IntoIteratorを実装する(省力版)
    use std::{array, slice};

    struct Array(pub [T; N]);

    impl Array {

    pub fn iter(&self) -> slice::Iter { self.0.iter() }

    pub fn iter_mut(&mut self) -> slice::IterMut { self.0.iter_mut() }

    }

    impl IntoIterator for Array {

    type Item = T;

    type IntoIter = array::IntoIter;

    fn into_iter(self) -> Self::IntoIter { IntoIterator::into_iter(self.0) }

    }

    impl<'a, T, const N: usize> IntoIterator for &'a Array {

    type Item = &'a T;

    type IntoIter = slice::Iter<'a, T>;

    fn into_iter(self) -> Self::IntoIter { self.iter() }

    }

    impl<'a, T, const N: usize> IntoIterator for &'a mut Array {

    type Item = &'a mut T;

    type IntoIter = slice::IterMut<'a, T>;

    fn into_iter(self) -> Self::IntoIter { self.iter_mut() }

    }

    fn main() {

    let mut arr = Array([0, 1, 2]);

    // 0, 1, 2

    for x in &arr {

    println!("{}", x);

    }

    for x in &mut arr {

    *x *= 2;

    }

    // 0, 2, 4

    for x in arr {

    println!("{}", x);

    }

    }

    Copyright © OPTiM Corp. All Rights Reserved. 39

    View Slide

  40. さいごに
    Copyright © OPTiM Corp. All Rights Reserved. 40

    View Slide

  41. イテレーターあるある
    イテレーターを使う時は便利系メソッドを見逃しがちで、

    あとから「もっと簡単に書けたやんけ!」となりがちで落ち込みがち。
    作る時は任意で実装するべきトレイト・メソッドがいくつもあって面倒がち。

    でもそのおかげで高いパフォーマンスが実現しがち。
    イテレーター色んな人が解説しがち。
    イテレーターは正しく使って正しく作って良い感じのプログラムを作りましょう。
    Copyright © OPTiM Corp. All Rights Reserved. 41

    View Slide

  42. Q&A
    Copyright © OPTiM Corp. All Rights Reserved. 42

    View Slide

  43. 他の言語にあってRustにないイテレーターの機能
    Q. 他の言語にありがちだがRustの標準ライブラリにはない機能にどんなものがあるか?
    A. 文字列に結合する、いわゆるjoinは標準ライブラリにはないもののitertoolsクレートには join メソッドがある。

    標準ライブラリとしては今後[interspace]メソッドによって近しいことが出来るようになる予定。
    Python
    print(', '.join(['a', 'b', 'c'])) # 'a, b, c'

    JavaScript
    console.log(["a", "b", "c"].join(", ")); // "a, b, c"

    Rust(itertools)
    use itertools::Itertools;

    println!("{}", ["a", "b", "c"].iter().join(", "));

    Rust(Nightly)
    #![feature(iter_intersperse)]

    println!("{}", ["a", "b", "c"].iter().copied().intersperse(", ").collect::()); // "a, b, c"

    Copyright © OPTiM Corp. All Rights Reserved. 43

    View Slide

  44. オプションのイテレーター化
    Q. オプションをイテレーターで使える件について、

    C++にそれがあったらforで回せそうだがRustでそういう使い方はする?
    std::optional op;

    for (auto& x : op) {

    std::cout << x << std::endl;

    }

    A. Rustにはif-let構文があるので使うことは無さそう。
    let v = Some(0);

    for x in &v {

    println!("{}", x);

    }

    if let Some(x) = &v {

    println!("{}", x);

    }

    Copyright © OPTiM Corp. All Rights Reserved. 44

    View Slide

  45. 非同期イテレーター
    Q. JavaScriptにある AsyncIterator のような仕組みがRustに導入される予定はあるか?
    A. futuresクレートに Stream トレイトがあり、現在はこれを使うことが出来る。

    Rust標準にも、将来的には Stream 及びそれを使ったasync for文が導入されることが検討されている。

    RFC 2996 Tracking Issue#79024
    Copyright © OPTiM Corp. All Rights Reserved. 45

    View Slide

  46. Rust 1.51のreduceメソッド
    Q. 他の言語では良くある reduce メソッドが1.51にもなってから導入されたのはなぜか?
    A. JavaScriptなどでの reduce はRust 1.0時点で fold メソッドとして実装されていた。

    1.51で実装された reduce メソッドは初期値をイテレーターの最初の値から得るもの。
    JavaScript
    console.log([1, 2, 3].reduce((a, b) => a + b, 0)); // 6 = 0 + 1 + 2 + 3

    Rust
    println!("{}", [1, 2, 3].iter().fold(0, |a, b| a + b)); // 6 = 0 + 1 + 2 + 3

    println!("{:?}", [1, 2, 3].iter().copied().reduce(|a, b| a + b)); // Some(6) = 1 + 2 + 3

    Copyright © OPTiM Corp. All Rights Reserved. 46

    View Slide

  47. Copyright © OPTiM Corp. All Rights Reserved. 47

    View Slide