Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding scopes in Ruby

I am trying to deal with a very weird (at least to me) situation in Ruby. My code is the following:

class ScopeTest
  attr_accessor :flag

  def flagtest
    puts "SELF: " + self.flag.to_s
    puts "INST: " + flag.to_s
    if flag == 1
      flag = 0
      puts "SELF: " + self.flag.to_s
      puts "INST: " + flag.to_s
      self.flag = 0
      puts "SELF: " + self.flag.to_s
      puts "INST: " + flag.to_s
      flagtest
    else
      puts "SELF: " + self.flag.to_s
      puts "INST: " + flag.to_s
    end
  end
end

st = ScopeTest.new
st.flag = 1
st.flagtest

And the output is the following:

SELF: 1
INST: 1
SELF: 1
INST: 0
SELF: 0
INST: 0
SELF: 0
INST: 0
SELF: 0
INST: 

What's weird is that the flag variable is nil the last time I print it (inside the else) but 0 straight before that (before the if...else in the flagtest method)?

When I remove a few lines of code, everything seems back to normal as the following code:

class ScopeTest
  attr_accessor :flag

  def flagtest
    puts "SELF: " + self.flag.to_s
    puts "INST: " + flag.to_s
    if flag == 1
      self.flag = 0
      puts "SELF: " + self.flag.to_s
      puts "INST: " + flag.to_s
      flagtest
    else
      puts "SELF: " + self.flag.to_s
      puts "INST: " + flag.to_s
    end
  end
end

st = ScopeTest.new
st.flag = 1
st.flagtest

gives the following output:

SELF: 1
INST: 1
SELF: 0
INST: 0
SELF: 0
INST: 0
SELF: 0
INST: 0

Any clues as to what and why is happening?

like image 279
sebkkom Avatar asked Mar 22 '23 13:03

sebkkom


2 Answers

It's because you have assignment to a local variable, flag = 0. IIRC, local variables are created at parse time and are scoped to the method. They are initialized with nil by default. So, in your then clause you assign it a value, before it is accessed. But when you re-enter the method, you go to else clause and flag is left uninitialized (that is, nil).

class ScopeTest
  attr_accessor :flag

  def flagtest
    # if you comment this line, the method will output 1, 1
    # if you don't comment, it will output 1, nil, because the assignment to local variable was never done.
    #   but local var itself was created.
    flag = 3 if false

    puts self.flag.inspect
    puts flag.inspect
  end
end

st = ScopeTest.new
st.flag = 1
st.flagtest
# >> 1
# >> nil
like image 76
Sergio Tulentsev Avatar answered Apr 02 '23 01:04

Sergio Tulentsev


flag = 0 creates a local variable called flag whose scope goes to the end of the method. Any use of flag that appears lexically after that assignment refers to the local variable. Any previous appearance (as well as any appearance of self.flag) refers to the getter method.

So the flag in the else refers to the local variable. Since the local variable never got a value in the else-branch, its value is nil.

like image 41
sepp2k Avatar answered Apr 02 '23 01:04

sepp2k