Does Iterator work on all methods that take blocks?

Iterator(T) - Crystal 1.3.2 mentions an Iterator working on Range#each if used without a block, does that trick similarly for any method that requires a block?

Are you asking if SomeClass.each will always return an Iterator if you don’t give it a block? If so, the answer is no. #each methods that return Iterator are specifically written for each type that implements it.

Just to follow up on this, I wanted to show a fairly typical example:

Range#each

  def each
    {% if B == Nil %}
      {% raise "Can't each beginless range" %}
    {% end %}

    if @begin.nil?
      raise ArgumentError.new("Can't each beginless range")
    end

    ItemIterator.new(self)
  end

(source)

This example shows that we can’t just have a block-less #each return an Iterator because implementing types (almost) always require their own custom iterators. In the case of Range, it’s ItemIterator, which is here, if you’re interested. If you want to learn more, especially if you’re implementing your own Iterator, I highly recommend looking through the module source to see the implementations of iterators there.

Note: all of the code and links here are for Crystal 1.3.2.

1 Like

Thanks!

As a point of feedback to the core people, it seems surprising to me that this code:

(1..10_000_000).select(&.even?)

behaves far differently than the iterator version:

(1..10_000_000).each.select(&.even?)

If it were me I’d try to make it more clear somehow, to readers…maybe add the word lazy to the name?

(1..10_000_000).lazy_each.select(&.even?)

To make it clearer? Even though that’s less ruby-like. Cheers!