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?
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"]
The block inherits the scope from the context it is defined in. Take a look at this example:
def give_me_a_proc
test = "foo"
lambda { puts test }
end
test = "bar"
give_me_a_proc.call
# => "foo"
So it doesn't matter where the proc/block is executed, but rather where it was defined.
In your case the food
variable inside the block is indeed shadowing the food
array from outside. But you really operate on the actual elements (not duplicates/clones) of food
array in the block so any changes to them will be reflected outside.
The reason why food
array changes into "Wine"
string after the block is finished is that the block operates in the same scope the food
array was defined, so it overwrites it with the last element of the array since it is the last object that is assigned to food
variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With