I was trying to figure out how Ruby handles local variables that have the same names as the methods in self
class, and found a behavior that I do not understand:
class A
def val
10
end
def test
val = val
end
end
p A.new.test
this code prints nil
. why?!
In a word, yes. Variable names only hold in the scope they're defined in, and you can use the same name in different scopes.
You cannot declare two variables with the same name in the same scope.
Answer: Yes — the scopes will not overlap so there will be two local variables, one per method, each with the same name.
It is usually not a good programming practice to give different variables the same names. If a global and a local variable with the same name are in scope, which means accessible, at the same time, your code can access only the local variable.
I think that the local variable is declared as soon as it's enunciated. In ruby the lookup is first to look for a local variable, if it exists it's used, and if not it looks for a method. This would mean that val = val declares the first val as local and the left-hand val then matches it (not sure about it I should check the ruby under microscope to be sure)
If you try
class A
def val
10
end
def test
back = []
x = val
back << x
val = x + 1
back << val
x = val
back << x
end
end
p A.new.test
then all is good, it prints [10, 11, 11] which means the first x = val calls the method, the second calls the local variable, presumably.
The other answers explain everything fine I just want to point readers with the same problem to the official documentation about this.
Local Variables and Methods
In Ruby local variable names and method names are nearly identical. If you have not assigned to one of these ambiguous names ruby will assume you wish to call a method. Once you have assigned to the name ruby will assume you wish to reference a local variable.
The local variable is created when the parser encounters the assignment, not when the assignment occurs:
a = 0 if false # does not assign to a p local_variables # prints [:a] p a # prints nil
The similarity between method and local variable names can lead to confusing code, for example:
def big_calculation 42 # pretend this takes a long time end big_calculation = big_calculation()
Now any reference to
big_calculation
is considered a local variable and will be cached. To call the method, useself.big_calculation
.You can force a method call by using empty argument parentheses as shown above or by using an explicit receiver like
self
. Using an explicit receiver may raise aNameError
if the method's visibility is not public or the receiver is the literalself
.Another commonly confusing case is when using a modifier
if
:p a if a = 0.zero?
Rather than printing “true” you receive a NameError, “undefined local variable or method `a'”. Since ruby parses the bare
a
left of theif
first and has not yet seen an assignment toa
it assumes you wish to call a method. Ruby then sees the assignment toa
and will assume you are referencing a local method.The confusion comes from the out-of-order execution of the expression. First the local variable is assigned-to then you attempt to call a nonexistent method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With