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

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

5c097a7b75c8802b073774faa7276503?s=47 optim
July 29, 2021

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

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

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

5c097a7b75c8802b073774faa7276503?s=128

optim

July 29, 2021
Tweet

Transcript

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

    Copyright © OPTiM Corp. All Rights Reserved. 1
  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
  3. 目次 1. イテレーター入門 2. イテレーターの使い方 3. イテレーターの作り方 4. さいごに 5.

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

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

    Reserved. 5
  6. 入門の前に:iterの読み方 Rustではイテレーターを iter と略すことが多いです。 ここでは国際熱核融合実験炉に従って「イーター」と呼んでいます。 https://ja.wikipedia.org/wiki/ITER Copyright © OPTiM Corp.

    All Rights Reserved. 6
  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
  8. イテレーターのおさらい または関数型プログラミングスタイルで使うもの let v = vec![1, 2, 3]; v.iter() //

    Iterator<Item = &u32> .copied() // Iterator<Item = u32> .filter(|x| *x % 2 == 1) // Iterator<Item = u32> .map(|x| x * 2) // Iterator<Item = u32> .for_each(|x| println!("{}", x)); // -> 2, 6 イテレーターとは・・・ 値を1つずつ取り出して処理(=「反復」「イテレート(iterate)」)するためのもの 日本語ではイテレーターのことを反復子と呼ぶ(JIS規格より) Copyright © OPTiM Corp. All Rights Reserved. 8
  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
  10. Rustのイテレーターの特徴 1. 回す時は手続き型(for文)でも関数型( for_each などでメソッドチェーン)でも使える 読みやすい方で書ける 2. 遅延評価 JavaScriptだと const

    v = [1, 2, 3]; v // -> Array<number> .filter(x => x % 2 == 1) // -> Array<number> .map(x => console.log(x)) // -> Array<void> ; // 1, 3 Rustだと let v = vec![1, 2, 3]; v.iter() // -> Iterator<Item = &u32> .copied() // -> Iterator<Item = u32> .filter(|x| *x % 2 == 1) // -> Iterator<Item = u32> .map(|x| println!("{}", x)) // -> Iterator<Item = ()> ; // 何も出力されない // 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
  11. イテレーターは何者だ イテレーターとは Iterator トレイトを実装する型 Range は Iterator を実装するが、 Vec は実装しない

    つまり Vec そのものはイテレーターではない pub trait Iterator { // ループする値の型 type Item; // 次の値を取得する fn next(&mut self) -> Option<Self::Item>; // ... } impl<A: Step> Iterator for ops::Range<A> { /* ... */ } // 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
  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
  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
  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
  15. IntoIteratorの定義 pub trait IntoIterator { // Iteratorでループする値の型 type Item; //

    変換後のイテレーターの型 type IntoIter: Iterator<Item = Self::Item>; // 値をイテレーターに変換する fn into_iter(self) -> Self::IntoIter; } Iterator には IntoIterator が自動で実装される。 これによりイテレーター自体をforループの対象に出来る。 impl<I: Iterator> 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
  16. イテレーターの使い方 Copyright © OPTiM Corp. All Rights Reserved. 16

  17. 3パターンのイテレーター Vec を始め HashMap や BTreeMap などのコレクション型には IntoIterator が3パターン実装されており、 使い方によって所有権の扱いが異なる。

    また、それぞれの IntoIterator と対応するメソッドも用意されており、関数型スタイルで使うことも出来る。 Copyright © OPTiM Corp. All Rights Reserved. 17
  18. 3パターンのイテレーター:ムーブ 値をムーブするので中身の所有権を奪うことが出来る。 let v: Vec<u32> = vec![1, 2, 3]; //

    xの型はu32 for x in v { /* ... */ } for x in v.into_iter() { /* ... */ } // 関数型スタイル v.into_iter().for_each(|x| { /* ... */ }); impl<T, A: Allocator> IntoIterator for Vec<T, A> { type Item = T; type IntoIter = IntoIter<T, A>; fn into_iter(self) -> IntoIter<T, A> { /* ... */ } } Copyright © OPTiM Corp. All Rights Reserved. 18
  19. 3パターンのイテレーター:借用 借用するので値を再利用出来る。 大抵の場合 IntoIterator は iter() を呼び出しているだけ。 let v: Vec<u32>

    = 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<T, A> { 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
  20. 3パターンのイテレーター:可変借用 可変借用するので値を変更出来る。 大抵の場合 IntoIterator は iter_mut() を呼び出しているだけ。 let mut v:

    Vec<u32> = 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<T, A> { 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
  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
  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
  23. その他のイテレーター Iterator 以外のトレイトや Iterator のメソッドではない自由関数、 イテレーターとして使うことを思いつかない型を紹介。 Copyright © OPTiM Corp.

    All Rights Reserved. 23
  24. ExactSizeIterator pub trait Iterator { fn size_hint(&self) -> (usize, Option<usize>)

    { /* ... */ } } 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
  25. DoubleEndedIterator pub trait DoubleEndedIterator: Iterator { fn next_back(&mut self) ->

    Option<Self::Item>; // ... } 双方向イテレーターである場合に実装される。 つまり最後の要素を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
  26. FusedIterator pub trait FusedIterator: Iterator {} イテレーターが None を返したら、その後もずっと None

    を返し続けることを保証するトレイト。 実は Iterator::next() は None を返したあとに Some(v) を返しても良いと規定している イテレーターが None を返したあとでも None を返し続けることに依存するコードを書く場合、 Iterator::fuse() を呼び出すことで None を返し続ける挙動にすることが出来る。 ( FusedIterator に対して fuse を呼び出した場合でも呼び出しオーバーヘッドがないように実装されている) fn hoge<I: Iterator<Item = u32>>(mut iter: I) { // イテレーターを最後まで進める while let Some(_) = iter.next() {} println!("{:?}", iter.next()); // Noneである保証はない } fn fuga<I: Iterator<Item = u32>>(iter: I) { let mut iter = iter.fuse(); while let Some(_) = iter.next() {} println!("{:?}", iter.next()); // 常にNone } Copyright © OPTiM Corp. All Rights Reserved. 26
  27. FromIterator pub trait FromIterator<A>: Sized { fn from_iter<T: IntoIterator<Item =

    A>>(iter: T) -> Self; } イテレーターを元に構築出来る型に実装される。 これを実装することで Iterator::collect() が使えるようにな る。 impl FromIteartor<I> for X とすると、要素が I 型のイテレーターから X を構築出来る、という意味。 Rustの標準ドキュメント(https://doc.rust-lang.org/std/iter/trait.FromIterator.html)を見ると 実装されている型が大量にあることが分かる。 1. FromIterator<char> for String 2. FromIterator<Result<A, E>> for Result<V, E> 要素が Result<T, E> の場合に Result<Vec<T>, E> で受け取るような形 3. FromIterator<Option<A>> for Option<V> 要素が Option<T> の場合に Option<Vec<T>> で受け取るような形 4. FromIterator<AsRef<Path>> for PathBuf ["a", "b", "c"] から PathBuf("a/b/c") で受け取るような形 Copyright © OPTiM Corp. All Rights Reserved. 27
  28. FromIterator Iterator<Item=u32> から Vec<u32> を構築する let v: Vec<u32> = [1,

    2, 3].iter().copied().collect(); println!("{:?}", v); // [1, 2, 3] Iterator<Item=char> から String を構築する let v: String = ['a', 'b', 'c'].iter().copied().collect(); println!("{:?}", v); // "abc" Iterator<Item=Result<u32, u32>> から Result<Vec<u32>, u32> を構築する let v: Result<Vec<u32>, u32> = [Ok(1), Ok(2), Ok(3)].iter().copied().collect(); println!("{:?}", v); // Ok([1, 2, 3]) let v: Result<Vec<u32>, u32> = [Ok(1), Ok(2), Err(3)].iter().copied().collect(); println!("{:?}", v); // Err(3) Iterator<Item=AsRef<Path>> から PathBuf を構築する let v: std::path::PathBuf = ["a", "b", "c"].iter().collect(); println!("{}", v.display()); // "a/b/c" Copyright © OPTiM Corp. All Rights Reserved. 28
  29. std::iter::empty pub const fn empty<T>() -> Iterator<Item = T> 何も返さない、空のイテレーターを生成する。

    let mut iter = std::iter::empty::<u32>(); 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
  30. std::iter::from_fn pub fn from_fn<T, F>(f: F) -> Iterator<Item = T>

    where F: FnMut() -> Option<T> 指定されたクロージャーからイテレーターを生成する。 Iterator::next がクロージャーとして実装されるイメージ。 let iter = std::iter::from_fn(|| { match rand::random::<i32>() { i if i < 0 => None, i => Some(i), } }); // 乱数が負の値になるまで値を出力する for x in iter { println!("{}", x); } Copyright © OPTiM Corp. All Rights Reserved. 30
  31. std::iter::once / once_with pub fn once<T>(value: T) -> Iterator<Item =

    T> pub fn once_with<A, F>(gen: F) -> Iterator<Item = A> where F: FnOnce() -> A 指定された値(クロージャーから返された値)を1度返すイテレーターを生成する。 // イテレーター生成時に乱数を得る let mut iter = std::iter::once(rand::random::<u32>()); println!("{:?}", iter.next()); // Some(?) println!("{:?}", iter.next()); // None // イテレーター呼び出し時に乱数を得る let mut iter = std::iter::once_with(rand::random::<u32>); println!("{:?}", iter.next()); // Some(?) println!("{:?}", iter.next()); // None Copyright © OPTiM Corp. All Rights Reserved. 31
  32. std::iter::repeat / repeat_with pub fn repeat<T>(elt: T) -> Iterator<Item =

    T> where T: Clone pub fn repeat_with<A, F>(repeater: F) -> Iterator<Item = A> where F: FnMut() -> A 指定された値(クロージャーを呼び出して得られる値)を無限に繰り返すイテレーターを生成する。 let mut iter = std::iter::repeat(rand::random::<i32>()); // どちらも同じ値 println!("{:?}", iter.next()); println!("{:?}", iter.next()); let iter = std::iter::repeat_with(rand::random::<i32>) .take_while(|i| *i >= 0); // 乱数が負の値になるまで値を出力する for x in iter { println!("{}", x); } Copyright © OPTiM Corp. All Rights Reserved. 32
  33. std::iter::successors pub fn successors<T, F>(first: Option<T>, succ: F) -> Iterator<Item

    = T> where F: FnMut(&T) -> Option<T> 初期値からクロージャーを呼び出して値を計算し続けるイテレーターを生成する。 雰囲気は 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
  34. 意外なイテレーター Option と Result は IntoIterator を実装しているのでイテレーターとして扱える。 let mut v

    = vec![0u32]; v.extend(Some(1)); // 追加される v.extend(None::<u32>); // 追加されない v.extend(Ok::<u32, ()>(2)); // 追加される v.extend(Err::<u32, ()>(())); // 追加されない println!("{:?}", v); // [0, 1, 2] once と empty を条件で呼び変える代わりにも使える。 fn get1() -> impl Iterator<Item = u32> { if rand::random::<bool>() { Box::new(std::iter::once(rand::random::<u32>())) as Box<dyn Iterator<Item = u32>> } else { Box::new(std::iter::empty()) as Box<dyn Iterator<Item = u32>> } } fn get2() -> impl Iterator<Item = u32> { if rand::random::<bool>() { Some(rand::random()) } else { None }.into_iter() } Copyright © OPTiM Corp. All Rights Reserved. 34
  35. イテレーターの作り方 Copyright © OPTiM Corp. All Rights Reserved. 35

  36. Iteratorを実装する 最低限実装しなければならないアイテム pub trait Iterator { type Item; fn next(&mut

    self) -> Option<Self::Item>; } 無限に値を返すイテレーターを作ってみる struct Hoge; impl Iterator for Hoge { type Item = u32; fn next(&mut self) -> Option<Self::Item> { 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
  37. カウントダウンするイテレーター struct CountDown(pub u32); impl Iterator for CountDown { type

    Item = u32; fn next(&mut self) -> Option<Self::Item> { 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
  38. 他に実装しておくべきもの pub trait Iterator { fn size_hint(&self) -> (usize, Option<usize>)

    { /* ... */ } } イテレーターの最小個数、または最大個数が分かる場合に実装する。 pub trait ExactSizeIterator: Iterator { fn len(&self) -> usize { /* ... */ } } 個数が特定出来る場合に実装する。 Iterator::size_hint と共に実装するべきである。 len にデフォルト実装はあるものの、 size_hint を参照して保証が保たれているかをアサートするものであるため、自分で実装するべきである。 pub trait DoubleEndedIterator: Iterator { fn next_back(&mut self) -> Option<Self::Item>; } 双方向イテレーターである場合に実装する。 next_back は最後の要素をイテレーターから除去して返す。 pub trait FusedIterator: Iterator {} イテレーターが None を返したあと、ずっと None を返し続ける場合に実装する。 マーカートレイトであり、実装すべきメソッドはない。 Copyright © OPTiM Corp. All Rights Reserved. 38
  39. IntoIteratorを実装する(省力版) use std::{array, slice}; struct Array<T, const N: usize>(pub [T;

    N]); impl<T, const N: usize> Array<T, N> { pub fn iter(&self) -> slice::Iter<T> { self.0.iter() } pub fn iter_mut(&mut self) -> slice::IterMut<T> { self.0.iter_mut() } } impl<T, const N: usize> IntoIterator for Array<T, N> { type Item = T; type IntoIter = array::IntoIter<T, N>; fn into_iter(self) -> Self::IntoIter { IntoIterator::into_iter(self.0) } } impl<'a, T, const N: usize> IntoIterator for &'a Array<T, N> { 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<T, N> { 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
  40. さいごに Copyright © OPTiM Corp. All Rights Reserved. 40

  41. イテレーターあるある イテレーターを使う時は便利系メソッドを見逃しがちで、 あとから「もっと簡単に書けたやんけ!」となりがちで落ち込みがち。 作る時は任意で実装するべきトレイト・メソッドがいくつもあって面倒がち。 でもそのおかげで高いパフォーマンスが実現しがち。 イテレーター色んな人が解説しがち。 イテレーターは正しく使って正しく作って良い感じのプログラムを作りましょう。 Copyright © OPTiM

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

  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::<String>()); // "a, b, c" Copyright © OPTiM Corp. All Rights Reserved. 43
  44. オプションのイテレーター化 Q. オプションをイテレーターで使える件について、 C++にそれがあったらforで回せそうだがRustでそういう使い方はする? std::optional<int> 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
  45. 非同期イテレーター Q. JavaScriptにある AsyncIterator のような仕組みがRustに導入される予定はあるか? A. futuresクレートに Stream トレイトがあり、現在はこれを使うことが出来る。 Rust標準にも、将来的には

    Stream 及びそれを使ったasync for文が導入されることが検討されている。 RFC 2996 Tracking Issue#79024 Copyright © OPTiM Corp. All Rights Reserved. 45
  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
  47. Copyright © OPTiM Corp. All Rights Reserved. 47