Search code examples
rubyscopevariable-assignmentblock

Block scope in ruby


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?


Solution

  • 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"]