Ruby

Enumerable and Enumerator in Ruby: each, Lazy, and Custom Classes

Enumerable and Enumerator in Ruby: each, Lazy, and Custom Classes

To make a Ruby class enumerable, define an each method that yields every element and include Enumerable. Start each with return to_enum(:each) unless block_given? so it returns an Enumerator when called without a block. You get Enumerable’s 61 methods (map, select, sort_by, lazy) for free, and the returned enumerator adds with_index chaining and external iteration with next.

That is the whole recipe; the rest of this guide builds it on a linked list from scratch, then covers Enumerator objects, lazy enumerators, what Ruby 2.6 to 3.4 added, and the error messages you meet along the way. Every transcript on this page was regenerated on Ruby 3.4.10; the complete method lists live in Ruby’s Enumerable, Enumerator, and Enumerator::Lazy documentation.

Enumerable, #each and Enumerator

Enumeration means traversing a collection. In Ruby, an object is enumerable when it describes a set of items and a method to loop over each of them.

Ruby’s collections get their enumeration methods from the Enumerable module: Array, Hash, Range, Struct, Dir, IO, File, Set, and Enumerator itself all include it. On Ruby 3.4.10 the module has 61 instance methods, among them include?, count, map, select, sort_by, each_slice, group_by, tally, and lazy.

The built-in classes do not always use the module’s versions. Array overrides 29 of the 61 with its own faster implementations, map, select, sum, sort, and include? among them; Hash overrides nine and Range 10. Even on an array, though, each_slice, each_cons, group_by, tally, filter_map, sort_by, and lazy come straight from Enumerable:

Ruby
[Array, Hash, Range, Struct, Dir, IO, File, Set, Enumerator].all? { |c| c.include?(Enumerable) } # => true
Enumerable.instance_methods.size # => 61
Enumerable.instance_methods(false).count { |m| Array.instance_method(m).owner == Array } # => 29
Array.instance_method(:map).owner # => Array
Array.instance_method(:each_slice).owner # => Enumerable

Every one of those methods is built on a single method the including class has to provide: each. Called with a block on an array, each runs the block for every element and returns the array:

Ruby
[1, 2, 3].each { |i| puts "* #{i}" }
# * 1
# * 2
# * 3
# => [1, 2, 3]

Called without a block, each returns an instance of Enumerator instead:

Ruby
[1, 2, 3].each # => #<Enumerator: [1, 2, 3]:each>

An Enumerator describes how to iterate over an object without doing it yet. It lets you step through the elements by hand with next, and it lets you chain enumeration:

Ruby
%w[dog cat mouse].each.with_index { |a, i| puts "#{a} is at position #{i}" }
# dog is at position 0
# cat is at position 1
# mouse is at position 2
# => ["dog", "cat", "mouse"]
 
%w[dog cat mouse].each.with_index(1).to_a # => [["dog", 1], ["cat", 2], ["mouse", 3]]

with_index shows how chained enumerators work. each is called without a block and returns an enumerator; with_index wraps that enumerator in another one that hands the block each element together with its index, counting from 1 when you ask it to.

Enumerable vs. Enumerator: What’s the Difference?

Enumerable is a module you mix into a class that defines each; it supplies the collection methods (map, select, sort, sum, and the rest of the 61). Enumerator is a class whose instances wrap a single iteration, so you can chain with_index onto it, step through it with next, or turn it lazy. The two meet in the middle: every Enumerator is itself Enumerable, and a lazy enumerator is an Enumerator subclass:

Ruby
Enumerator.include?(Enumerable) # => true
Enumerator::Lazy.ancestors.take(4) # => [Enumerator::Lazy, Enumerator, Enumerable, Object]

Most Enumerable methods take a block. If the difference between a block, a proc, and a lambda is fuzzy, the lambda vs. proc vs. block table in our lambdas guide is the reference.

Making Objects Enumerable

Under the hood, methods like max, map, and take rely on each. A plain-Ruby stand-in for max looks like this:

Ruby
def max
  max = nil
 
  each do |item|
    if !max || item > max
      max = item
    end
  end
 
  max
end

Internally, Enumerable’s methods have C implementations, but the stand-in shows the shape: each loops over every value, the method remembers the highest one it has seen, and returns it at the end. map works the same way, with a block:

Ruby
def map
  new_list = []
 
  each do |item|
    new_list << yield(item)
  end
 
  new_list
end

map yields every item to the passed block and collects the results in a new list, which it returns once each is done.

Implementing #each

Implement each, include Enumerable, and a class receives min, take, inject, and the rest for free. Most classes can delegate to an array they already hold (@items.each(&block)), so the interesting case is a structure with no array to lean on: a linked list.

Linked lists: lists without arrays

A linked list is a collection of data elements, in which each element points to the next. Each element in the list has two values, named the head and the tail. The head holds the element’s value, and the tail is a link to the rest of the list.

Shell
[42, [12, [73, nil]]]

For a linked list with three values (42, 12, and 73), the first element’s head is 42, and the tail is a link to the second element. The second element’s head is 12, and the tail holds the third element. The third element’s head is 73, and the tail is nil, which indicates the end of the list.

In Ruby, a linked list is a class with two instance variables, @head and @tail:

Ruby
class LinkedList
  def initialize(head, tail = nil)
    @head, @tail = head, tail
  end
 
  def <<(item)
    LinkedList.new(item, self)
  end
 
  def inspect
    [@head, @tail].inspect
  end
end

<< adds a value by returning a new list with the passed value as the head and the previous list as the tail. inspect is there so you can look inside the list:

Ruby
LinkedList.new(73) << 12 << 42 # => [42, [12, [73, nil]]]

Now for each. It takes a block and runs it for every value in the list. The list’s recursive shape does most of the work: yield the @head, then call each on the @tail if there is one. Returning self at the end mirrors Array#each, which returns the array it iterated over:

Ruby
class LinkedList
  def each(&block)
    yield @head
    @tail.each(&block) if @tail
    self
  end
end

Two things happen with the block here. yield calls it implicitly: the method never names the block, Ruby runs it with the value you pass and hands back its result, and a yield inside a method that was called without a block raises LocalJumpError (covered in the errors section). The &block parameter is the explicit form: it captures the same block as a Proc object, which is what lets the recursive call pass it along to @tail.each. A method can use either or both. block_given? reports whether a block arrived at all, which is how the guard that returns an enumerator decides what to do. With a block, each prints the values in order and returns the list:

Ruby
list = LinkedList.new(73) << 12 << 42
list.each { |item| puts item }
# 42
# 12
# 73
# => [42, [12, [73, nil]]]

Now that the list responds to each, include Enumerable makes it enumerable:

Ruby
class LinkedList
  include Enumerable
end
Ruby
list.count # => 3
list.max # => 73
list.map { |item| item * item } # => [1764, 144, 5329]
list.select(&:even?) # => [42, 12]

What You Get for Free From Enumerable

With each in place, all 61 methods work on the list. A sample, every line showing its output:

Ruby
list.sort # => [12, 42, 73]
list.include?(12) # => true
list.first(2) # => [42, 12]
list.sum # => 127
list.min_by { |item| -item } # => 73
list.sort_by { |item| -item } # => [73, 42, 12]
list.filter_map { |item| item * 2 if item.even? } # => [84, 24]
list.tally # => {42 => 1, 12 => 1, 73 => 1}
list.group_by(&:even?) # => {true => [42, 12], false => [73]}
list.each_slice(2).to_a # => [[42, 12], [73]]
list.each_cons(2).to_a # => [[42, 12], [12, 73]]
list.chunk_while { |a, b| b < a }.to_a # => [[42, 12], [73]]
list.slice_when { |a, b| b > a }.to_a # => [[42, 12], [73]]
list.zip([1, 2, 3]) # => [[42, 1], [12, 2], [73, 3]]
list.each_with_object([]) { |item, acc| acc.unshift(item) } # => [73, 12, 42]
list.each_entry.to_a # => [42, 12, 73]
list.to_set # => #<Set: {42, 12, 73}>
list.lazy # => #<Enumerator::Lazy: [42, [12, [73, nil]]]>

Two details from that list. to_set needs no require "set" on Ruby 3.4: Set autoloads, and Enumerable#to_set is defined in Ruby’s prelude. And sort_by orders by the block’s result but makes no promise about the order of elements that tie, so do not depend on it for equal keys.

sum deserves a mention too. Enumerable#sum uses compensated summation for floats, so it gives the answer inject(:+) cannot:

Ruby
([0.1] * 10).sum # => 1.0
([0.1] * 10).inject(:+) # => 0.9999999999999999

Returning Enumerator Instances

Most chaining already works. Enumerable’s methods build their own enumerator when called without a block, and only call each once something consumes it, so list.map.with_index.to_h, list.each_slice(2), list.each_with_index, and list.lazy all run against the each written so far:

Ruby
list.map # => #<Enumerator: [42, [12, [73, nil]]]:map>
list.map.with_index.to_h # => {42 => 0, 12 => 1, 73 => 2}

What does not work is a bare list.each. The method yields unconditionally, so calling it without a block, directly or through list.each.next or list.each.with_index, raises no block given (yield) (LocalJumpError). Array#each returns an Enumerator in that situation, and every Ruby reader expects a custom each to do the same. The idiom is one guard line at the top of the method:

Ruby
class LinkedList
  def each(&block)
    return to_enum(:each) unless block_given?
 
    yield @head
    @tail.each(&block) if @tail
    self
  end
end

to_enum wraps the object in an Enumerator that calls the named method, each, when the enumerator is consumed. enum_for is the same method under a second name:

Ruby
list.each # => #<Enumerator: [42, [12, [73, nil]]]:each>
list.each.class # => Enumerator
list.enum_for(:each) # => #<Enumerator: [42, [12, [73, nil]]]:each>
Kernel.instance_method(:to_enum) == Kernel.instance_method(:enum_for) # => true

With the guard in place, the list chains through each the way an array does, and it supports external iteration with next, peek, and rewind:

Ruby
list.map.with_index(1) { |item, i| "#{i}. #{item}" } # => ["1. 42", "2. 12", "3. 73"]
list.each.with_index(1).to_a # => [[42, 1], [12, 2], [73, 3]]
 
e = list.each
e.next # => 42
e.peek # => 12
e.next # => 12
e.next # => 73
e.next
# => iteration reached an end (StopIteration)
e.rewind
e.next # => 42

One thing a custom each does not give the enumerator is a size. to_enum accepts a block for that, and it calls the block on the object to_enum was called on, so inside each the guard can read return to_enum(:each) { count } unless block_given? and list.each.size becomes 3. Without the block, the size is unknown:

Ruby
list.each.size # => nil
list.to_enum(:each) { list.count }.size # => 3

A caveat if you subclass: << hardcodes LinkedList.new, so SizedList.new(73) << 12 returns a plain LinkedList and a subclass’s each override never runs on the list you end up holding. Write self.class.new(item, self) in << if you plan to subclass.

Working With Enumerator Objects

You do not need a collection to build an enumerator. Enumerator.new takes a block that receives an Enumerator::Yielder; every << (or yield) on the yielder hands one value to whoever is consuming the enumerator, and << returns the yielder so the calls chain:

Ruby
e = Enumerator.new { |y| y << 1; y << 2; y.yield 3 }
e.next # => 1
e.peek # => 2
e.next # => 2
e.next # => 3
e.next
# => iteration reached an end (StopIteration)
e.rewind
e.next # => 1
e.to_a # => [1, 2, 3]
e.size # => nil
Enumerator.new { |y| y << y.class }.next # => Enumerator::Yielder
Enumerator.new { |y| y << 1 << 2 }.to_a # => [1, 2]
Enumerator.new(3) { |y| 3.times { |i| y << i } }.size # => 3

next runs the block up to the first <<, suspends it there, and resumes it on the following call. Ruby implements that suspension with a Fiber, which our fibers and enumerators post takes apart. Calling next past the last value raises StopIteration; loop rescues that exception for you and returns the value the generator block ended with:

Ruby
e = Enumerator.new { |y| y << 1; y << 2; :done }
loop { e.next } # => :done

Because nothing forces the block to finish, an enumerator can be infinite:

Ruby
fib = Enumerator.new { |y| a, b = 0, 1; loop { y << a; a, b = b, a + b } }
fib.take(10) # => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
fib.lazy.select(&:even?).first(5) # => [0, 2, 8, 34, 144]

take(10) stops asking after 10 values. fib.select(&:even?) without lazy never returns: select is eager and tries to consume the whole sequence before it hands anything back, which is what the lazy section fixes.

Three more ways to build enumerators arrived after this post was first written. Enumerator.produce (Ruby 2.7) generates a sequence from a start value and a block; chain and Enumerator#+ (2.6) join enumerables end to end; Enumerator.product (3.2) yields every combination, with the last argument iterating fastest, so it may be endless:

Ruby
Enumerator.produce(1) { |n| n * 2 }.take(5) # => [1, 2, 4, 8, 16]
[1, 2].chain([3], 4..5).to_a # => [1, 2, 3, 4, 5]
([1, 2].each + [3]).to_a # => [1, 2, 3]
[1, 2].chain([3]).class # => Enumerator::Chain
Enumerator.product([1, 2], [3, 4]).to_a # => [[1, 3], [1, 4], [2, 3], [2, 4]]
Enumerator.product([1, 2], [3, 4]).class # => Enumerator::Product
Enumerator.product([1, 2], [3, 4]).size # => 4
Enumerator.product([1, 2], (1..)).first(3) # => [[1, 1], [1, 2], [1, 3]]

Since 2.7 the yielder also converts to a proc, so a generator block can feed an existing collection straight into it:

Ruby
Enumerator.new { |y| [1, 2].each(&y) }.to_a # => [1, 2]

Lazy Enumerators With Enumerator::Lazy

lazy turns any enumerable into an Enumerator::Lazy: an Enumerator whose map, select, reject, take_while, and friends return another lazy enumerator instead of an array. Nothing runs until something asks for values, which is why an infinite source works:

Ruby
(1..).lazy.map { |i| i * 2 } # => #<Enumerator::Lazy: #<Enumerator::Lazy: 1..>:map>
(1..).lazy.map { |i| i * 2 }.select { |i| i % 3 == 0 }.first(3) # => [6, 12, 18]

(1..) is an endless range (Ruby 2.6); (1..Float::INFINITY) gives the same result. Without lazy, (1..).map tries to build an infinite array and never returns.

The proof that lazy evaluation does less work is a counter. Eager map touches all 20 elements before select sees any of them; the lazy chain pulls elements one at a time and stops as soon as first(2) has its two results:

Ruby
touched = 0
(1..20).map { |i| touched += 1; i * 2 }.select { |i| i % 3 == 0 }.first(2)
touched # => 20
 
touched = 0
(1..20).lazy.map { |i| touched += 1; i * 2 }.select { |i| i % 3 == 0 }.first(2)
touched # => 6

To get an array back, call to_a or its alias force. eager (2.7) stops short of that: it returns a regular, non-lazy Enumerator, so the next map produces an array instead of another lazy enumerator:

Ruby
(1..5).lazy.map { |i| i * 2 }.to_a # => [2, 4, 6, 8, 10]
(1..5).lazy.map { |i| i * 2 }.force # => [2, 4, 6, 8, 10]
(1..5).lazy.map { |i| i * 2 }.map { |i| i + 1 }.class # => Enumerator::Lazy
(1..5).lazy.map { |i| i * 2 }.eager.class # => Enumerator
(1..5).lazy.map { |i| i * 2 }.eager.map { |i| i + 1 } # => [3, 5, 7, 9, 11]

The lazy versions of filter_map (2.7) and compact (3.1) exist too, with_index on a lazy enumerator has been lazy since 2.7, and zip, each_slice, and take_while all stop early on an endless source:

Ruby
(1..).lazy.filter_map { |i| i * 2 if i.even? }.first(3) # => [4, 8, 12]
(1..).lazy.map { |i| i if i.odd? }.compact.first(3) # => [1, 3, 5]
(1..).lazy.with_index.map { |i, idx| i * idx }.first(4) # => [0, 2, 6, 12]
(1..).lazy.zip("a"..).first(3) # => [[1, "a"], [2, "b"], [3, "c"]]
(1..).lazy.each_slice(2).first(2) # => [[1, 2], [3, 4]]
(1..).lazy.take_while { |i| i < 4 }.to_a # => [1, 2, 3]

Streams are the other natural fit. File.foreach without a block returns an Enumerator that reads a line at a time, and lazy on it keeps that behavior through the whole chain, so this stops reading after the third match no matter how large the file is:

Ruby
File.write("lines.txt", (1..1000).map { |i| "line #{i}" }.join("\n"))
File.foreach("lines.txt").class # => Enumerator
File.foreach("lines.txt").lazy.class # => Enumerator::Lazy
File.foreach("lines.txt").lazy.grep(/7$/).map(&:chomp).first(3) # => ["line 7", "line 17", "line 27"]

The lines keep their newlines until chomp, which is why the pattern is /7$/ rather than /7\z/. Our post on slurping and streaming files covers the IO side of that in detail.

The custom class gets all of this for free, because Enumerable#lazy only needs each:

Ruby
list.lazy.map { |item| item * 2 }.select(&:even?).first(2) # => [84, 24]

When to Use Lazy Enumerators

Reach for lazy when:

  • The source is infinite or unbounded: an endless range, a generator, Enumerator.produce.
  • The source is a stream: a file, a socket, a paginated API.
  • A chain of map and select only needs its first few results, so stopping early saves the rest of the work.
  • The input is large and the intermediate array each eager step would allocate matters.

Skip it for small in-memory arrays. Eager methods are simpler and faster there. In a rough benchmark, a map, select, sum chain over a million-element array ran about two to three times slower lazily than eagerly, because every element passes through a chain of blocks instead of one tight loop. The eager and lazy enumeration section of our follow-up post walks through the internals behind that difference.

What Ruby 2.6 to 3.4 Added to Enumerable and Enumerator

The first version of this post was written against Ruby 2.5. Every row here was probed on the official Docker image of each version (present on the stated version, absent on the one before) and matched to the NEWS entries:

Landed inAdditionVerified on 3.4.10
2.6Enumerable#filter (alias of select), Enumerable#chain, Enumerator#+, Enumerator::Chain, endless ranges (1..)[1, 2].chain([3], 4..5).to_a # => [1, 2, 3, 4, 5]
2.7Enumerable#filter_map, Enumerable#tally, Enumerator.produce, Enumerator::Lazy#eager, Enumerator::Lazy#filter_map, a lazy Enumerator::Lazy#with_index, Enumerator::Yielder#to_proc%w[a b a].tally # => {"a" => 2, "b" => 1}; Enumerator.produce(1, &:succ).take(3) # => [1, 2, 3]
3.0The two-argument Enumerator.new(obj, :each) form is removedEnumerator.new([1, 2, 3], :each) raises tried to create Proc object without a block (ArgumentError); write [1, 2, 3].to_enum(:each) instead
3.1Enumerable#compact, Enumerator::Lazy#compact, Enumerable#tally with a hash to add into, each_slice and each_cons return the receiver (they returned nil), Array#intersect?%w[a b].tally({ "a" => 1 }) # => {"a" => 2, "b" => 1}; [1, 2, 3].each_slice(2) { } # => [1, 2, 3]; [1, 2].intersect?([2, 3]) # => true
3.2Enumerator.product and Enumerator::Product; Set autoloads, so to_set needs no require "set"Enumerator.product([1, 2], [3, 4]).to_a # => [[1, 3], [1, 4], [2, 3], [2, 4]]
3.3No new Enumerable or Enumerator methods; NoMethodError messages gain for an instance of FooEnumerable.instance_methods.size # => 61 (55 on 2.5, 59 on 2.7)
3.4No new Enumerable or Enumerator methods; Hash#inspect prints {42 => 0}; error messages switch to straight quoteslist.map.with_index.to_h # => {42 => 0, 12 => 1, 73 => 2}

Two things did not make the table because they are not what they look like. tally_by does not exist; the 3.1 change is tally accepting a hash to add into. And Comparable#clamp with a range (2.7) is Comparable, not Enumerable.

Common Errors When Making a Class Enumerable

Each message here is the exact text Ruby 3.4.10 prints for a file run, so you can match it against your terminal. Your file and line numbers will differ; the message, the class in parentheses, and the frame names will not. The caret lines come from error_highlight, a default gem that underlines the expression responsible for the exception.

no block given (yield) (LocalJumpError)

each uses yield, and something called it without a block: a bare list.each, list.each.next, or list.each.with_index. yield has nothing to call, so Ruby raises before the first element:

Ruby
class LinkedList
  include Enumerable
 
  def initialize(head, tail = nil)
    @head, @tail = head, tail
  end
 
  def <<(item)
    LinkedList.new(item, self)
  end
 
  def each(&block)
    yield @head
    @tail.each(&block) if @tail
    self
  end
end
 
list = LinkedList.new(73) << 12 << 42
list.each
Shell
$ ruby list.rb
list.rb:13:in 'LinkedList#each': no block given (yield) (LocalJumpError)
	from list.rb:20:in '<main>'

The fix is the guard: return to_enum(:each) unless block_given? as the first line of each. error_highlight prints no caret block for a LocalJumpError, so the two-line report is all you get.

undefined method 'call' for nil (NoMethodError)

The same mistake in the other spelling. When each captures the block as &block and calls block.call, a missing block arrives as nil, and the error names call instead of yield:

Ruby
class LinkedList
  include Enumerable
 
  def initialize(head, tail = nil)
    @head, @tail = head, tail
  end
 
  def <<(item)
    LinkedList.new(item, self)
  end
 
  def each(&block)
    block.call(@head)
    @tail.each(&block) if @tail
  end
end
 
list = LinkedList.new(73) << 12 << 42
list.each
Shell
$ ruby list.rb
list.rb:13:in 'LinkedList#each': undefined method 'call' for nil (NoMethodError)

    block.call(@head)
         ^^^^^
	from list.rb:19:in '<main>'

Add the same return to_enum(:each) unless block_given? guard, or switch to yield, which works with &block still in the signature for the recursive call. Ruby 3.4 prints for nil; older versions printed for nil:NilClass, so search results quoting that form describe the same error.

undefined method 'map' for an instance of LinkedList (NoMethodError)

each is written, the guard is in place, and include Enumerable was forgotten. Nothing supplies map, so Ruby reports it missing on the class and did_you_mean offers the nearest name it knows:

Ruby
class LinkedList
  def initialize(head, tail = nil)
    @head, @tail = head, tail
  end
 
  def <<(item)
    LinkedList.new(item, self)
  end
 
  def each(&block)
    return to_enum(:each) unless block_given?
 
    yield @head
    @tail.each(&block) if @tail
    self
  end
end
 
list = LinkedList.new(73) << 12 << 42
list.map { |item| item * item }
Shell
$ ruby list.rb
list.rb:20:in '<main>': undefined method 'map' for an instance of LinkedList (NoMethodError)

list.map { |item| item * item }
    ^^^^
Did you mean?  tap

Add include Enumerable to the class. The Did you mean? tap line is a red herring: tap is the closest method name to map on a class that has no Enumerable methods at all.

undefined method 'each' for an instance of LinkedList (NoMethodError)

The mirror image: include Enumerable is there and each is not. Enumerable#map is implemented in C and calls each on the receiver, which is why the frame reads Enumerable#map while the caret sits under .map, the Ruby call site that started it:

Ruby
class LinkedList
  include Enumerable
 
  def initialize(head, tail = nil)
    @head, @tail = head, tail
  end
 
  def <<(item)
    LinkedList.new(item, self)
  end
end
 
list = LinkedList.new(73) << 12 << 42
list.map { |item| item * item }
Shell
$ ruby list.rb
list.rb:14:in 'Enumerable#map': undefined method 'each' for an instance of LinkedList (NoMethodError)

list.map { |item| item * item }
    ^^^^
	from list.rb:14:in '<main>'

Define each. Every Enumerable method (to_a, first, lazy, sort) fails with this same message until the class has one.

iteration reached an end (StopIteration)

External iteration ran out of elements. next raises StopIteration when the enumerator has nothing left, and the fourth call on a three-element enumerator is one too many:

Ruby
enumerator = [42, 12, 73].each
enumerator.next
enumerator.next
enumerator.next
enumerator.next
Shell
$ ruby list.rb
list.rb:5:in 'Enumerator#next': iteration reached an end (StopIteration)
	from list.rb:5:in '<main>'

Rescue StopIteration, call rewind to start over, or let loop handle it: loop rescues StopIteration and returns the value the underlying each returned, the array in this case:

Ruby
enumerator = [42, 12, 73].each
loop { puts enumerator.next }
# 42
# 12
# 73
# => [42, 12, 73]

wrong argument type Integer (must respond to :each) (TypeError)

zip (and the other methods that combine collections) was handed something that is not enumerable. The message names the type it got and the method it needed:

Ruby
[42, 12, 73].zip(3)
Shell
$ ruby list.rb
list.rb:1:in 'Array#zip': wrong argument type Integer (must respond to :each) (TypeError)

[42, 12, 73].zip(3)
                 ^
	from list.rb:1:in '<main>'

Pass an enumerable: an array, a range, or an object with each. [1, 2].zip(3..4) returns [[1, 3], [2, 4]].

tried to create Proc object without a block (ArgumentError)

Old tutorials wrote Enumerator.new(object, :each) to wrap an object in an enumerator. That two-argument form was deprecated in 2.x and removed in Ruby 3.0, so on every current version Enumerator.new demands a block and treats the arguments as a size:

Ruby
Enumerator.new([1, 2, 3], :each)
Shell
$ ruby list.rb
list.rb:1:in 'Enumerator#initialize': tried to create Proc object without a block (ArgumentError)

Enumerator.new([1, 2, 3], :each)
               ^^^^^^^^^^^^^^^^
	from list.rb:1:in 'Class#new'
	from list.rb:1:in '<main>'

Write [1, 2, 3].to_enum(:each) (or enum_for) instead; that is what the old form did internally. A bare Enumerator.new with no arguments and no block raises the same message without the caret block.

Three more messages come from Enumerable methods on data they cannot compare or add. [42, "12", 73].max raises comparison of String with 42 failed (ArgumentError), [42, nil, 73].sort raises comparison of NilClass with 73 failed (ArgumentError), and [42, nil, 73].max raises comparison of Integer with nil failed (ArgumentError); the fix is compact for the nils, or a key every element supports, such as sort_by(&:to_s), which orders the mixed list as ["12", 42, 73]. sum over strings raises String can't be coerced into Integer (TypeError), because the default starting value is 0; pass the starting value you mean, ["a", "b"].sum(""), and you get "ab".

Six Lines of Code and an Include

The complete class, as built on this page:

Ruby
class LinkedList
  include Enumerable
 
  def initialize(head, tail = nil)
    @head, @tail = head, tail
  end
 
  def <<(item)
    LinkedList.new(item, self)
  end
 
  def inspect
    [@head, @tail].inspect
  end
 
  def each(&block)
    return to_enum(:each) unless block_given?
 
    yield @head
    @tail.each(&block) if @tail
    self
  end
end

That is the whole enumerable linked list: a six-line each and an include. The guard line makes each behave like Array#each, the yield and the recursive call do the iteration, and Enumerable supplies its 61 methods, lazy included. From that point on the class chains, iterates externally with next, and feeds chain and Enumerator.product like any built-in collection.

Frequently asked questions

How do I make a Ruby class enumerable?
Define an each method that yields each element, then include Enumerable in the class. Enumerable calls your each to implement map, select, sort, sum, and the rest. Start each with return to_enum(:each) unless block_given? so calling it without a block returns an Enumerator instead of raising LocalJumpError.
What is the difference between Enumerable and Enumerator in Ruby?
Enumerable is a module you mix into a class that defines each; it supplies collection methods like map and select. An Enumerator is an object that wraps one iteration so you can chain with_index, step through it with next, or make it lazy. Every Enumerator is itself Enumerable.
When should I use lazy enumerators in Ruby?
Use lazy when the source is infinite or too large to hold in memory, when you read from a stream such as a file, or when a chain of map and select only needs its first few results. For small in-memory arrays, eager methods are simpler and faster.
How do I return an Enumerator when no block is given in Ruby?
Add return to_enum(:each) unless block_given? as the first line of each. Kernel#to_enum, also called enum_for, wraps the object in an Enumerator that calls each later. Pass a block to to_enum to give the enumerator a size, for example to_enum(:each) { count }.
What does iteration reached an end mean in Ruby?
It is the StopIteration message raised when you call next on an Enumerator that has no elements left. Rescue StopIteration, call rewind to start over, or use loop, which rescues StopIteration for you and returns the enumerator’s final value.

Published , Updated

Wondering what you can do next?

  • Share this article on social media

Become our next author!

Find out more
$appsignal install

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal