LaunchKit
All quizzes
Ruby 23 views

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

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
  1. i = 42 creates the local variable i.
  2. for i in (1...10) iterates over 1 through 9 (three dots exclude the upper bound), assigning each value to that same i.
  3. After the loop, i holds 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.

Only used to confirm and publish your comment. Never shown publicly, never shared.

Markdown: **bold**, `code`, ```fenced blocks```, > quotes, [links](url). HTML and images are not rendered.