🔄

イテレータとクロージャ — Rubyistが最も親しみを感じる領域

select/map/reduceがfilter/map/foldに変わっただけ

Ruby開発者がRustで初めて「おっ、これは馴染みがある」と感じる部分がイテレータだ。

メソッド対応表

Ruby Rust 説明
each for_each 各要素に実行
map map 変換
select / filter filter 条件フィルタ
reject `filter(\ x\
reduce / inject fold 累積
find find 最初のマッチ
any? any 1つでも満たす?
all? all 全て満たす?
count count 個数
flat_map flat_map 平坦化 + 変換
zip zip 並列走査
take(n) take(n) 先頭n個
min / max min / max 最小/最大
sum sum 合計
sort_by sort_by (Vec) ソート

基本チェーン

# Ruby
users.select { |u| u.active? }
     .map { |u| u.name }
     .sort

// Rust
users.iter()
    .filter(|u| u.is_active())
    .map(|u| &u.name)
    .sorted()  // itertools crate必要、またはcollect後にsort
    .collect::<Vec<_>>()

核心的な違い:Rustでは最後に.collect()を呼ばないと結果が作られない。イテレータはlazyに動作する。Rubyのlazy列挙子がRustではデフォルト。

クロージャ構文

# Ruby — ブロック
[1, 2, 3].map { |x| x * 2 }

# Ruby — lambda
double = ->(x) { x * 2 }
[1, 2, 3].map(&double)

// Rust — クロージャ
vec![1, 2, 3].iter().map(|x| x * 2).collect::<Vec<_>>();

// 変数に保存
let double = |x: &i32| x * 2;
vec![1, 2, 3].iter().map(double).collect::<Vec<_>>();

{ |x| }|x|。パイプがブロックの区切りを代替する。

fold — reduceのRust版

# Ruby
[1, 2, 3, 4].reduce(0) { |sum, x| sum + x }  # 10

// Rust
vec![1, 2, 3, 4].iter().fold(0, |sum, x| sum + x);  // 10

// sum()でもっと簡単に
vec![1, 2, 3, 4].iter().sum::<i32>();  // 10

enumerate — each_with_index

# Ruby
["a", "b", "c"].each_with_index { |item, i| puts "#{i}: #{item}" }

// Rust
vec!["a", "b", "c"].iter().enumerate().for_each(|(i, item)| {
    println!("{}: {}", i, item);
});

iter, into_iter, iter_mut

Rubyではeachがあればいい。Rustには3種類のイテレータがある。

  • iter() — 参照で走査(&T)、元データ維持

  • iter_mut() — 可変参照で走査(&mut T)、元データ変更可能

  • into_iter() — 所有権を取る、元データ使用不可

大抵の場合iter()を使えばいい。

キーポイント

1

select → filter、reduce → fold、each_with_index → enumerate

2

イテレータはlazy — .collect()を呼ばないと結果が出ない

3

クロージャ構文: { |x| } → |x|

4

3種類: iter(参照)、into_iter(所有権)、iter_mut(可変)

メリット

  • Ruby開発者にとって最も直感的なRust機能
  • lazyデフォルト動作で大量データでもメモリ効率的

デメリット

  • .collect()を忘れると何も起こらない
  • iter/into_iter/iter_mutの選択が最初は混乱

ユースケース

データフィルタリング/変換パイプライン RubyのEnumerableチェーンをRustにポーティングする時