4

I'm kind of confused about how scoping works in Ruby. Which Ruby scoping rules prevent the following code from printing out the value of x?

#!/usr/bin/ruby

x = 5
def test
    puts "x = #{x}"
end

test    # => undefined local variable or method `x' for main:Object

The equivalent Python script does print the value of x:

#!/usr/bin/python

x = 5
def test():
    print "x = %s" % x

test()  # => x = 5
jvm_update
  • 265
  • 2
  • 9

2 Answers2

5

This is so called "scope gate". There are three gates in ruby: definition of a method, a class or a module. Meaning that inside that definition locals from outer scope are not visible.

This is so because when scope changes, current set of bindings gets replaced by a new set of bindings and cannot be easily accessed.

See "Metaprogramming Ruby: Program Like the Ruby Pros" for more information.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • The new version of this book is [Metaprogramming Ruby 2: Program Like the Ruby Pros](https://pragprog.com/book/ppmetr2/metaprogramming-ruby) – Ben Fulton Jun 04 '15 at 16:21
3

See "Ruby classes, include, and scope" for more information about Ruby's scoping and scope gating.

There are a few ways to provide visibility to your test function. One is to make the variable an instance variable:

#!/usr/bin/ruby

@x = 5
def test
    puts "x = #{@x}"
end

And another is to use a closure with lambda:

#!/usr/bin/ruby

x = 5
test = lambda do
    puts "x = #{x}"
end

test.call

or Proc.new()

#!/usr/bin/ruby

x = 5
test = Proc.new do
    puts "x = #{x}"
end

test.call

For the differences between lambda and Proc.new, see "When to use lambda, when to use Proc.new?".

Community
  • 1
  • 1
Zach
  • 2,441
  • 1
  • 16
  • 20