liveBook · Manning

Scope

As explained in The Well-Grounded Rubyist, Fourth Edition

Scope is the lexical region of a Ruby program in which a variable or other name is visible and can be resolved.

Suppose a method assigns `x = "Hello"` and calls another method that assigns `x = "Goodbye"`. When the first method resumes, its `x` is still `"Hello"`. The names match, but each method call has its own local variables. Scope is the reach or visibility of an identifier: knowing the current scope tells you which variable or constant a name can denote. section 2.5 section 5.2

The boundary depends on the kind of identifier:

  • Local variables belong to a local scope. The top level and each `class`, `module`, or `def` definition start separate local scopes; each method call gets a fresh set of locals. section 5.2.2
  • Ordinary global variables, marked with `$`, remain available across those boundaries. Instance variables instead belong to the object that is `self`; they are self-bound rather than scope-bound. section 5.2.1 section 5.2
  • Constants depend on their enclosing classes or modules. A qualified path such as `M::C::X` identifies one explicitly; a leading `::` starts lookup at the top level. section 5.2.4
class C
  a = 1          1
  def local_a
    a = 2        2
    puts a
  end
  puts a       3
end
c = C.new
c.local_a      4

The `a` assigned in the class body prints as `1`. The `a` assigned inside `local_a` prints as `2`: entering `def` created a different local scope, even though the two assignments sit close together. section 5.2.2

Scope is not the same as `self`. Scope governs visibility; `self` is the current object. A recursive method call can keep the same `self` while receiving fresh local variables, leaving the earlier call’s values intact. section 5.2 section 5.2.3

Blocks add a useful contrast. A block can read and reassign a local that already exists outside it, so the outer value can change. But a block parameter with the same name is a different variable: assigning to that parameter does not reassign the outer local. This behavior also matters when blocks become closures. section 6.3.8

An assignment that never runs

After `if false; x = 1; end`, inspecting `x` yields `nil`, while inspecting an otherwise unknown `y` raises an error. Ruby’s parser recognized `x = 1` and allocated a local even though the conditional body did not execute. Because bare words can also be method calls, this parse-time distinction matters when a name appears to be missing. section 6.1.2 section 2.5.4

When a name resolves unexpectedly, first locate the enclosing definitions and check whether it is a block parameter. Then distinguish locals from globals, constants, and variables belonging to `self`. `local_variables` lists currently defined local names, while `rdbg` can display locals and their values at a breakpoint. section 15.3.1 section 1.4.5

How The Well-Grounded Rubyist, Fourth Edition covers it

How deep it goes
Works through definition boundaries and block-variable behavior, including parameter shadowing; defers closure behavior to a later treatment.
Examples
Contrasts separate `a` values in a class and method, then tests whether a block parameter changes an outer `x`.
What it assumes
Assumes familiarity with Ruby assignments, methods, classes, and blocks while developing local-scope boundaries through examples.
What you can do after
Diagnose/debug with scope boundaries and `rdbg` to distinguish same-named locals and inspect their values at a breakpoint.
How it connects
Connects local variables and constants with `self`, global variables, block parameters, and closures.

Also explained in

Compare how 21 books explain scope →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Well-Grounded Rubyist, Fourth Edition. The section links point to the parts of the book it draws on.