Enumerable.each vs 'for' loops in Ruby

A few days ago I checked the code using the Ruby project roodi, and I received a warning saying “Don’t use ‘for’ loops. Use Enumerable.each instead.” I personally do not see any difference between these two versions of cycles, and attributed the problem of choice to issues of personal faith. I asked people in the !ruby group of identi.ca, but the answers were not convincing. Google also did not shed light on this question.

I changed the code to use Enumerable.each, just in case, and put the problem aside. But today, I found a sample code, that clearly demonstrates the difference:

loop1 = []
loop2 = []

calls = ["one", "two", "three"]

calls.each do |c|
  loop1 << Proc.new { puts c }
end

for c in calls
  loop2 << Proc.new { puts c }
end

loop1[1].call #=> "two"
loop2[1].call #=> "three"

Published on March 08, 2010 (over 7 years ago)

Article tags: loop, ruby

Comments (5)

ShadowBelmolve over 7 years ago

Strange...

loop1[1].call #=> "two"
loop2[1].call #=> "three"

ree 1.8.7

Paul Philippov over 7 years ago

Sorry. my fault, The original code had these values. I've simplified the code but overlooked the output in comments. I changed the post to have correct values.

Marty Andrews over 7 years ago

I wrote the Roodi check because Enumerable.each seems to be more idiomatic of Ruby code than for loops do. I commonly see people writing for loops when they're new to the language. For me, it *was* an issue of personal choice. People could always turn the check off if they don't like it.

I honestly wasn't aware of the differences you described above. Good to know though - thanks!

Paul Philippov over 7 years ago

Marty, thank you for the Roodi. The more I use it, the more I like it =)

Scott W. Bradley over 6 years ago

The difference is that #each scopes 'c' inside the block, whereas for/in scopes it outside the block. Your example is a bit tricky because of the indirection of the proc. In the #each case, each Proc has a different 'c' because that is scoped inside that block only. The for/in case, each Proc is sharing the same 'c' because it was scoped outside the loop -- hence they all get the value "three".

Another demonstration of this:

for i in [1,2,3]
# ...
end
puts i # => 3

[1,2,3].each do |j|
# ...
end
puts j # => NameError: undefined local...