Call an iterator without a block and you often get an Enumerator instead of an immediate result: `str.each_byte` returns an object ready to supply the string’s bytes, while `str.each_byte { |b| puts b }` performs the iteration. An enumerator is an object, not an iterator method or a container of copied elements. Its `each` obtains values from a defined iteration procedure, and Enumerable supplies methods such as `map`, `select`, and `take` on top of that procedure. section 10.9.3 section 10.9
There are three common ways to establish that procedure:
- Call `Enumerator.new` with a block receiving a yielder. Each `y << value` specifies a value to yield; traversal runs the block, so it can compute values rather than store a fixed collection. section 10.9.1
- Call `object.enum_for(:method)` or `object.to_enum(:method)` to attach `each` to a method that yields values. Omit the method name to use `:each`; additional arguments are passed to the attached method. section 10.9.2
- Let a blockless iterator return one implicitly. For example, `array.map` makes an enumerator whose `each` invokes the array’s `map` operation when given a block. section 10.9.3 section 10.10.1
e = Enumerator.new do |y|
y << 1
y << 2
y << 3
end
This small constructor supplies three values through the yielder; the resulting enumerator can be traversed with `to_a` or transformed with `map`. section 10.9.1
The attached method matters. `array.map` followed by the enumerator’s `each { |x| x.capitalize }` returns a mapped array, not the original array: that `each` is a front end to `map`. An enumerator also has its own Enumerable methods. Thus a hash’s `select` returns a hash, but `hash.to_enum.select { ... }` uses Enumerable’s `select` and returns an array of selected key-value pairs. An enumerator is not a general-purpose proxy for its source. section 10.10.1 section 10.13
Because an enumerator is an object, it can track progress. `e.next` obtains one value; `e.rewind` returns its position to the start. This permits stop-and-start traversal rather than requiring one complete iterator call. An enumerator can even make a non-Enumerable object usable with Enumerable methods: attach `enum_for(:play)` to an object whose `play` method yields notes, then call `map` or `select` on the enumerator. section 10.10.3 section 10.10.4
Enumerators also carry iteration instructions into method chains. `animals.each_slice(2).map { |first, second| ... }` hands pairs to `map` without first building an entire array of slices. By contrast, a bare `animals.map.select` has no mapping block to remember, so it offers little over `animals.select`. Check what an intermediate expression actually returns: slice-family methods return enumerators, and `to_a` collects their results when an array is needed. section 10.11.1 section 10.5.4
An enumerator can provide a limited protective view: returning `@cards.to_enum` lets callers iterate over a deck’s cards but not call the array’s `<<` on the returned object. It does not make the underlying array impossible to alter. Also watch captured state: arguments passed to `enum_for`, including a mutable starting string for `inject`, remain the same objects on later traversals. section 10.10.2 section 10.9.2
