Understanding the “NameError: undefined local variable or method” Error Message
During my days as a Junior Developer when I just started learning Ruby, I never paid attention to the structure of this error message until I was asked a question during one of my review sessions with my trainer. She asked what the error message means and what it has to do with how Ruby does local variable and method lookup. This article answers the question.
In Ruby, before you reference a local variable, you are expected to define it. The way you define a local variable is by assignment.
a = 1
puts a
=> 1
However, if you reference an undefined local variable, Ruby complains.
At the top-level (file or REPL);
> puts b
NameError: undefined local variable or method `b' for main:Object
And within an instance method;
class Cat def speak
puts sound
endend> Cat.new.speak
NameError: undefined local variable or method `sound' for #<Cat:0x007fc0a113f358>
If you take a good look at the error messages, you will see that local variables b
and sound
were also looked up as methods.
Now that’s interesting. The intention was to reference a local variable, but Ruby also checks if the variable is available as methods. This has to do with how Ruby performs a variable lookup.
Every time you reference a local variable, Ruby first checks if it was defined. If it’s not, it looks up the reference as a method on self
within the current context.
What is self?
In Ruby, self
is a special variable that points to the current object. The value of self
changes based on the current context of execution.
You can read more about it here.
Like the examples above, at the top-level, Ruby checks if the method b
is available on the current object main
. And within an instance method, it checks if the method sound
is available on the current object #<Cat:0x007fc0a113f358>
which is the instance of the Cat
class.
This is why it is possible to call a method on the current object within a specific context without explicitly calling it on self
class Cat def speak
puts sound
end def sound
'Meow'
endend> Cat.new.speak
=> Meow
Note that self.sound
would also give the same result. The main difference is that Ruby will not look it up as a local variable. This can also be seen in the error message.
> puts self.b
NoMethodError: undefined method `b' for main:Object
A NoMethodError
is raised this time around
class Cat def speak
puts self.sound
endend> Cat.new.speak
NoMethodError: undefined method `sound' for #<Class:0x007ffd88025110>
Conclusion
The way Ruby does variable lookup is what makes it possible to implicitly call a method on the current object. It saves you from littering your code base with self.
, thus keeping it clean and beautiful.
Ruby is a very interesting language. A lot of people who come from other programming languages find a lot of things strange about it. But as you begin to dig deeper into it, you’d come to love and appreciate it.