Blocks reach outer local variables
Question
What is the return value of the following Ruby code?
a = "outer"
1.times do
a = "inner"
end
a # => ???
The correct answer is
-
"outer" -
"inner"Correct -
nil
Explanation
TL;DR
A Ruby block does not open a fresh scope for variables that already exist. Because a was defined before the block, the a inside the block is the same local variable, so the assignment rebinds it to "inner" and the change is visible after the block returns.
Step by step
a = "outer"
1.times do
a = "inner"
end
a # => "inner"
a = "outer"creates the local variableain the outer scope.1.timesruns the block once. When the parser reads the block body,ais already a known local, soa = "inner"does not create a block-local variable; it reassigns the outer one.- After the block,
astill points at"inner".
The direction matters: blocks see locals from their enclosing scope, but a variable first assigned inside a block stays local to the block:
1.times do
b = "inner only"
end
defined?(b) # => nil
Edge cases
You can force a name to be block-local, even when an outer variable with the same name exists, by declaring it after a semicolon in the block parameters:
a = "outer"
1.times do |; a|
a = "inner"
end
a # => "outer"
Method definitions with def do not behave like blocks: a def body starts a brand-new scope and cannot see the surrounding locals at all. This closure behavior is specific to blocks, procs, and lambdas.
Share this quiz
Comments
No comments yet. Be the first.