πŸ”„

Iterators & Closures β€” Where Rubyists Feel Most at Home

select/map/reduce just became filter/map/fold

This is where Ruby developers first think "oh, this feels familiar" in Rust.

Method mapping table

Ruby Rust Note
each for_each execute on each
map map transform
select / filter filter condition filter
reject `filter(\ x\
reduce / inject fold accumulate
find find first match
any? any any satisfy?
all? all all satisfy?
count count count
flat_map flat_map flatten + transform
zip zip parallel iteration
take(n) take(n) first n
min / max min / max min/max
sum sum sum
sort_by sort_by (Vec) sort

Basic chaining

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

// Rust
users.iter()
    .filter(|u| u.is_active())
    .map(|u| &u.name)
    .sorted()  // needs itertools crate, or collect then sort
    .collect::<Vec<_>>()

Key difference: Rust requires .collect() at the end to materialize results. Iterators are lazy by default. Ruby's lazy enumerator is Rust's default.

Closure syntax

# Ruby β€” block
[1, 2, 3].map { |x| x * 2 }

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

// Rust β€” closure
vec![1, 2, 3].iter().map(|x| x * 2).collect::<Vec<_>>();

// stored in variable
let double = |x: &i32| x * 2;
vec![1, 2, 3].iter().map(double).collect::<Vec<_>>();

{ |x| } β†’ |x|. Pipes replace block delimiters.

fold β€” Rust's reduce

# 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

// simpler with 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 just has each. Rust has three kinds of iterators.

  • iter() β€” iterate by reference (&T), original preserved

  • iter_mut() β€” iterate by mutable reference (&mut T), can modify

  • into_iter() β€” takes ownership, original consumed

Most of the time, iter() is what you want.

Key Points

1

select β†’ filter, reduce β†’ fold, each_with_index β†’ enumerate

2

Iterators are lazy β€” must call .collect() to materialize results

3

Closure syntax: { |x| } β†’ |x|

4

Three kinds: iter (ref), into_iter (ownership), iter_mut (mutable)

Pros

  • Most intuitive Rust feature for Ruby developers
  • Lazy by default means memory efficient even with large datasets

Cons

  • Forget .collect() and nothing happens
  • Choosing iter/into_iter/iter_mut is confusing at first

Use Cases

Data filtering/transformation pipelines Porting Ruby Enumerable chains to Rust