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 believe me?
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 if the
x = 1
line will ever get executed.
Don’t confuse the parser with the interpreter. The parser does not care whether
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.