Ruby Enumerator::Lazy: Efficient Lazy Evaluation for Large Collections
When working with huge or infinite collections in Ruby, eager methods build full arrays and can exhaust memory. Ruby’s Enumerator::Lazy defers operations until a terminal call, letting you safely iterate millions of items. This blog shows how to use it, when it pays off, and what pitfalls to avoid.
29 Aug 2026, 07:07 UTC

Problem: Memory‑hungry Collections
When you call eager methods like map or select on a large range, Ruby builds an intermediate array that holds every element. For a range of millions of numbers or an infinite sequence, this can quickly exhaust available memory or never finish.
Thesis: Defer Work Until You Need It
Ruby’s Enumerator::Lazy wraps an enumerable and postpones operations such as map, select, reject, and flat_map until a terminal method (e.g., force, first, each) is called. The result is a streaming pipeline that processes items one at a time, keeping memory usage constant.
Lazy Enumerator Basics
- Requires Ruby 2.0 or newer –
ruby -vwill show the version. - Call
.lazyon any enumerable:(1..).lazycreates a lazy enumerator over the infinite range. - Chain lazy operators:
select { |n| n.even? },map { |n| n * 2 },take(10). - Terminal methods trigger evaluation:
forcereturns a real array,eachiterates without building an array.
Worked Example: First 10 Even Numbers from an Infinite Range
# Ensure Ruby 2.0+ is running
# irb or a script
lazy = (1..).lazy.select { |n| n.even? }.take(10)
puts lazy.force
Output:
2
4
6
8
10
12
14
16
18
20
Notice that no array of all odd numbers was created; the pipeline stops after the tenth even number is found.
Trade‑offs & Limitations
- Per‑element overhead: Each lazy operation adds a small function call cost, which can be noticeable in tight loops.
- Side‑effects are delayed: A
putsinside aselectblock only runs when the terminal method is invoked. This can hide bugs if you expect immediate output. - Non‑lazy methods break the chain: Calling
sort,max, orminmaxforces the entire sequence into memory, defeating the benefit. - Version requirement: Ruby < 2.0 does not support
.lazyand will raiseNoMethodError.
Actionable Guidance
- Use
.lazywhen dealing with large or infinite enumerables and you only need a subset or a streaming result. - Keep the pipeline free of early‑forcing methods; if you need a sorted result, materialize the data first.
- Test side‑effects by adding a
putsinside the block and observe that it executes only duringforceoreach. - Measure memory usage with tools like
psor Ruby’sObjectSpaceto confirm that the lazy pipeline keeps memory low. - Document the lazy behavior in code comments so future maintainers understand that the chain is deferred.
In short, Enumerator::Lazy is a powerful tool for Ruby developers who need to process large data streams efficiently. Apply it judiciously, keep an eye on side‑effects, and you’ll avoid the pitfalls of eager evaluation.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.