Why for i in (1...10) clobbers your i
Question
What's the return value of the following Ruby code?
i = 42
for i in (1...10)
puts i
end
i # => ???
The correct answer is
-
42 -
10 -
nil -
9Correct
Explanation
TL;DR
for does not create a new scope. The loop variable i is the same local variable as the i = 42 above it, reassigned on every iteration. The range 1...10 excludes 10, so the last assignment is i = 9, and that value survives the loop.
Step by step
i = 42
for i in (1...10)
end
i # => 9
i = 42creates the local variablei.for i in (1...10)iterates over1through9(three dots exclude the upper bound), assigning each value to that samei.- After the loop,
iholds the last assigned value,9.
With an inclusive range the answer changes accordingly:
i = 42
for i in (1..10)
end
i # => 10
for vs each
each with a block is the construct that scopes its variable. A block parameter is local to the block, so the outer i is untouched:
i = 42
(1...10).each do |i|
end
i # => 42
The absence of a scope in for cuts both ways: variables created inside the body also leak out, which is occasionally convenient and usually a source of bugs:
for x in [1, 2, 3]
y = x * 2
end
x # => 3
y # => 6
With each, both x and y would be gone after the block and referencing them would raise NameError. This scoping behavior is the main reason idiomatic Ruby prefers each over for.
Share this quiz
Comments
No comments yet. Be the first.