Local Variable Scoping Rules in Ruby- Part Two

Continuing on the topic of local variable scoping rules, we come to the scoping rules for methods.

Check this code:

a = 5

def some_method
  a = 3
end

puts a # => 5 (Why 5 and not 3?

When the code executes “puts a”, it displays 5 and not 3. The reason is our 2nd rule.

Rule #2 – Methods create their own scope outside of execution flow.

We’ll visit methods a little more later in the blog.

Blocks and Nests

Rule #3 – A block creates a new scope for local variables, nested blocks created nested scopes, and the scope of a variable is determined by where it is initiated.

Let’s take a look at the following code for illustration.

2.times do
  a = 'hello' # a is initialized inside the 2.times block.
  puts a
end

loop do
  puts a     # => uh oh NameError
  break
end

puts a      # => man! another NameError

The variable “a” is initialized in the first block and therefore can’t be accessed the second block or the last “puts a” line. That also means that peer blocks cannot access variables initialized in other blocks. Which also then means we could reuse “a” for another variable in the second block. We really shouldn’t use letters for variable names since they should be a lot more descriptive.

Nested blocks create nested scopes. If you initiate a variable outside of any blocks it will be accessible by blocks and inner blocks. If a variable is initiated in a block it is accessible by blocks created inside the block it was created in. Confused? I guess this concept is better served with actual code.

a = 1            # first level variable accessible by blocks and nested blocks

loop do
  b = 2          # second level variable

  loop do
    c = 3        # third level not accessible to first or second level
    puts a       # => 1
    puts b       # => 2
    puts c       # => 3
    break
  end
  
  puts a         # => 1
  puts b         # => 2
  puts c         # => NameError because c is on 3rd level
  break
end

puts a          # => 1
puts b          # => NameError because b is on 2nd level
puts c          # => NameError because c is on 3rd level

Variable Shadowing

Basically if you have an outer variable and an inner variable with the same name, you prevent the outer variable from being modified by the code where the inner variable exists.

n = 10
[1, 2, 3].each do |n|
  puts n    # will use the block 'n' and ignore the outer scoped 'n'
end
n = 10
1.times do |n|
  n = 11
end
puts n    # => 10 because the outer scope 'n' isn't changed

Rule #4 – The simplest way to avoid variable shadowing is to use descriptive variable names.

Variables and Methods

In contrast to blocks that leak from outer to inner blocks, methods are self contained.

a = 'hello'

def some_method
  puts a
end

# invoke the method
some_method     # => NameError because puts can't access 'a'
  
  def some_method(a)
    puts a
  end

  some_method(5)   # => 5

Rule #5 – Variables can’t leak into a method from outside the method, they have to be passed in explicitly.

Blocks Within Methods

Rule #6 – The rules of block scope remain in full effect inside of a method.

Constants

  USERNAME = 'Boss'

  def welcome
   puts "Welcome, #{USERNAME}!"  end

  welcome     # => Welcome, Boss!

Rule #7 – Constants are accessible by methods and can be initiated in an inner scope.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s