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 preservediter_mut()β iterate by mutable reference (&mut T), can modifyinto_iter()β takes ownership, original consumed
Most of the time, iter() is what you want.
Key Points
select β filter, reduce β fold, each_with_index β enumerate
Iterators are lazy β must call .collect() to materialize results
Closure syntax: { |x| } β |x|
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