Block scope in ruby

18bytes picture 18bytes · Jul 27, 2012 · Viewed 11.3k times · Source

My understanding was that ruby blocks have block scope, and all variables created inside block will live only within the block.

Example case:

 food = ['toast', 'cheese', 'wine']
 food.each { |food| puts food.capitalize}
 puts food

Output:

"Toast"
"Cheese"
"Wine"
"Wine"

If you take the food variable inside the block (Each block), my understanding was that it has block scope. It lives only within the block scope, and does not have any influence on the outer variable food.

But the behavior is different, the outer variable named food is modified in this case. Is this understanding correct, In ruby do we have block scope?

Answer

Sergio Tulentsev picture Sergio Tulentsev · Jul 27, 2012

This is expected behaviour for ruby 1.8. It was fixed in 1.9. Snippets below are run with ruby 1.9.3

food = ['toast', 'cheese', 'wine']
food.each { |food| puts food.capitalize.inspect} # !> shadowing outer local variable - food
puts food.inspect
# >> "Toast"
# >> "Cheese"
# >> "Wine"
# >> ["toast", "cheese", "wine"]

You are correct, food from the block is scoped to that block and shadows other variables with this name. But if you do something destructive to it, it will be reflected in the original array, because it is reference to array element, not its copy. Observe:

food = ['toast', 'cheese', 'wine']

food.each { |f| f.capitalize} # transform and discard
food # => ["toast", "cheese", "wine"]

food.each { |f| f.capitalize! } # transform destructively (bang-version)
food # => ["Toast", "Cheese", "Wine"]