Variable hoisting in Ruby
Table of Contents
Have you ever heard of hoisting? Well, regardless if you have or you have not, Ruby has an interesting hositing mechanism built-in. Let’s take a dive and see how it creates variables and do some experiments with it.
What is hoisting? Well, according to Google “hoist” means to raise something. Apparently, with with ropes and pulleys. At least, back in the day.
Well, when it comes to variable hoisting, it’s basically a mechanism by which the language, in our context - Ruby, declares and defines variables. Okay, that sounds cool. Well, there are couple of quirks in Ruby that you should be aware of.
Bit of weirdness #
Take this code for example, and run it in console:
# weird_1.rb puts x
We will get an error, obviously.
x is not defined therefore you get an error:
NameError: undefined local variable or method `x' for main:Object
That is normal. Next, try this:
# weird_2.rb if true x = 1 end puts x
This will output 1, which is expected. Now, let’s try something else:
# weird_3.rb if false x = 1 end puts x
What should this output? An error? Or nil? Or maybe 1, if somehow we live in a
parallel universe? If we try to run this code, we will get a
nil. You don’t
Try adding this to the bottom of the script:
# weird_4.rb puts x.class #=> NilClass
You see, if we try to call an undefined variable we will get a
But, if we define the variable in a part of the code that will not be run at
all we will get a nil.
What on Earth is going on here, right? Right?!
Well, it’s not that complicated really. But, it’s a quirk that most of us have not ran across. The “magic” here is done by Ruby’s parser.
Basically, when the parser runs over the if-clause (look in weird_3.rb example
file) it first declares the variable, regardless of whether it will actually be
executed. This means that when the parser sees
x=1, it will actually declare
the variable, by assigning it to
nil and let the interpreter than figure out
x = 1 line will ever get executed.
Don’t confuse the parser with the interpreter. The parser does not care whether
x ever gets a value. The job of the parser is just to go over the code, find
any local variables and allocate “space” for those variables. More
specifically, set them to
On the other hand, it’s the interpreter that will follow the logical path of
the program and see if/when
x will get a value and act on it.