img_array = [
[0, 1, 2, 3],
[4, 5, 6, 7],
[8, 9, 0, 1]
]
We have:
enum = img_array.each
#=> #<Enumerator: [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 0, 1]]:each>
Array#each therefore creates an enumerator that is an instance of the class Enumerator. The method Enumerator#each passes each element of enum to the block and assigns the block variables:
enum.each { |row, row_index| puts "row=#{row}, row_index=#{row_index}" }
# row=0, row_index=1
# row=4, row_index=5
# row=8, row_index=9
We can see what each of the elements of enum are by using the method Enumerator#next:
enum.next #=> StopIteration: iteration reached an end
Whoops! I forgot to reset the enumerator:
enum.rewind
#=> #<Enumerator: [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 0, 1]]:each>
enum.next #=> [0, 1, 2, 3]
enum.next #=> [4, 5, 6, 7]
enum.next #=> [8, 9, 0, 1]
enum.next #=> StopIteration: iteration reached an end
Alternatively, we could convert the enumerator to an array (no need to rewind):
enum.to_a #=> [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 0, 1]]
When the element ([0, 1, 2, 3]) is passed to the block, the block variables are assigned as follows:
row, row_index = [0, 1, 2, 3] #=> [0, 1, 2, 3]
row #=> 0
row_index #=> 1
If the variables were |row, row_index, col_index|, the assignment would be:
row, row_index, col_index = [0, 1, 2, 3] #=> [0, 1, 2, 3]
row #=> 0
row_index #=> 1
col_index #=> 2
If they were |row, row_index, *rest|, it would be:
row, row_index, *rest = [0, 1, 2, 3] #=> [0, 1, 2, 3]
row #=> 0
row_index #=> 1
rest #=> [2, 3]
This is called parallel (or multiple) assignment. You can read up on the rules for such assignments at the link I've given.