LaunchKit
All quizzes
Ruby 0 views

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

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"
  1. a = "outer" creates the local variable a in the outer scope.
  2. 1.times runs the block once. When the parser reads the block body, a is already a known local, so a = "inner" does not create a block-local variable; it reassigns the outer one.
  3. After the block, a still 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.

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.