Ruby Blocks and Iterators: Write Your Own
Learn how Ruby methods receive blocks, pass values with yield, handle a missing block, and return an Enumerator. Build and run your own iterator.

On this page
Ruby Blocks and Iterators: Write Your Own
You have probably written numbers.each { |number| puts number }. The interesting question is what happens on the method's side of that call. How does each hand a number to your block? What happens when nobody supplies a block?
This guide builds a small iterator so you can see both sides. If each, map, and select are still new to you, start with the Ruby arrays guide. The examples below were run with Ruby 3.3.

The method decides when the block runs
A block is code attached to a method call. Its parameters, between | characters, receive values from the method. Inside a method, yield calls that block:
1def twice
2 yield 1
3 yield 2
4end
5
6twice do |number|
7 puts "Visit #{number}"
8endVisit 1
Visit 2The caller provides the block; twice controls when it runs and which values it receives. The do ... end form is handy for several lines. Braces work for a short call: twice { |number| puts number }.
The value of a yield expression is the block's return value. A method can use it, as this tiny transformer does:
1def transform_two
2 [yield(1), yield(2)]
3end
4
5p transform_two { |number| number * 10 }[10, 20]This is the same idea behind transformations such as map: the method gathers values returned by the block. In contrast, an iterator can simply hand values over and ignore the block's return values.
Build an iterator for passing scores
Suppose a report should visit only scores at or above a chosen minimum. We can write each_passing to decide which scores reach the caller's block:
1def each_passing(scores, minimum)
2 return enum_for(__method__, scores, minimum) unless block_given?
3
4 scores.each do |score|
5 yield score if score >= minimum
6 end
7end
8
9scores = [48, 82, 67, 91]
10each_passing(scores, 70) { |score| puts "Passed: #{score}" }Passed: 82
Passed: 91Read the method from top to bottom:
block_given?asks whether this call includes a block.- With a block,
scores.eachwalks the input in order. The condition decides whether toyielda score. - The caller's block prints each score it receives. The method does not collect its return values. With a block, this implementation returns
scores, becausescores.eachreturns its receiver.

The arrow back matters: after the block runs for 82, each_passing resumes its loop and can yield 91. The block does not take over the whole iteration; the method remains in charge of which values arrive and when.
The minimum is an ordinary method argument. The block is additional behavior supplied by the caller. Neither the method nor the input array needs to know whether the caller will print, count, or save the scores.
No block? Return something useful
Without the first line of each_passing, reaching yield with no block would raise LocalJumpError. Returning an Enumerator instead lets the caller decide how to consume the values later:
1passing = each_passing([48, 82, 67, 91], 70)
2
3p passing.class
4p passing.to_a
5p passing.firstEnumerator
[82, 91]
82enum_for(__method__, scores, minimum) creates an enumerator for this method and remembers its arguments. When to_a or first asks for values, the enumerator calls each_passing with a block. It does not create the filtered array at the point where passing is assigned. Each consumption runs the method again, so changes to the original input can affect later results.
This two-way interface—yield to a supplied block, or return an enumerator—is common in Ruby's collection methods. If your method requires a block, you can omit the guard and document that requirement instead. Choose the behavior deliberately.
Try it yourself
Write each_long_word(words, minimum_length). It should yield words whose length is at least the minimum and return an enumerator if called without a block. Try each_long_word(%w[cat otter elephant], 5).to_a before looking at the answer.
1def each_long_word(words, minimum_length)
2 return enum_for(__method__, words, minimum_length) unless block_given?
3
4 words.each do |word|
5 yield word if word.length >= minimum_length
6 end
7end
8
9p each_long_word(%w[cat otter elephant], 5).to_a["otter", "elephant"]The key idea is simple: a block belongs to the caller, while the method chooses when to call it. Once you can write that method, built-in iterators feel less mysterious.
Further reading
Read next

C++ std::vector: Size, Capacity, and Safe Growth
Learn how std::vector grows in C++: the difference between size and capacity, when reserve helps, why resize is different, and how growth can invalidate references.

How to Handle Errors in Go: A Hands-On Guide
Learn Go's everyday error pattern with runnable examples: return an error, check it, add context with %w, and recognize a missing file with errors.Is.

What Is the Swift Programming Language? A Simple Guide to the Language Behind iPhone Apps
A friendly, jargon-free guide to Swift, the programming language Apple created for iPhone, iPad and Mac apps. Learn why it replaced Objective-C, how optionals prevent crashes, how an app is made, and how fast Swift really is.
Comments
Be kind and specific. Comments with links from new accounts appear after a moderator reviews them.