Video transcript & code
Ruby goes to great lengths to be a low ceremony language. For instance, in Ruby there is no need to declare the types of variables or methods. There is no need to declare the instance variables in a class. And there is no need to declare local variables before they are assigned.
As an example, we can reference instance variables or global variables that don't exist.
@foo # => nil $bar # => nil
Rather than raising an exception, Ruby treats these variables as if they have values of
It might seem as if what is happening here is that Ruby is spontaneously causing these variables to exist the moment they are referenced. The technical term for this kind of spontaneous generation is autovivification. Meaning that a variable is "automatically brought to life".
But if we ask Ruby whether the instance variable we just referenced exists, it claims it does not.
And if we ask for a list of the current instance variables, Ruby says there are none.
@foo # => nil instance_variable_defined?("@foo") # => false instance_variables # => 
So what is happening here is not really autovivification. Ruby is not causing these variables to be brought to life. Instead, it simply treats any missing instance variable as if it has a value of
As you no doubt know, Ruby behaves a little differently when it comes to local variables. Trying to reference a nonexistent local variable results in a name error.
foo # => # ~> NameError # ~> undefined local variable or method `foo' for main:Object # ~> # ~> xmptmp-in26383sFG.rb:1:in `<main>'
And this is probably for the best. If Ruby silently returned
nil for every unrecognized local variable name, we'd have a terrible time hunting down typos.
However, there is a case where Ruby does, in fact, autovivify local variables. And if you don't know what's going on, you might be taken by surprise by Ruby's behavior.
Here's an example.
We assign a local variable
We write an
if statement, saying that if
x is even, a new variable
result should be set to the the value of
x divided by two.
Then we check the value of
Not surprisingly, the value is
And if we ask Ruby for a list of local variables, we see exactly what we expect:
x = 2 if x.even? result = x / 2 end result # => 1 local_variables # => [:x, :result]
Now let's set
3. Before we execute this, let's think about what we expect to happen.
x is now odd, we expect that the
if body will not be executed. Since it won't be executed, we expect that the
result variable will not be assigned. Since it is never assigned, we expect that this variable will not be defined, and accessing it will raise a
NameError just like we saw before.
Let's check our assumptions.
x = 3 if x.even? result = "even" end result # => nil
This is not what we expected!
Instead of a
NameError, we see that
result has a value of
Has Ruby suddenly started treating local variables the way it treats instance variables? Is it now acting as if any unset local variable has a value of
Let's check. We'll ask Ruby what local variables are defined.
x = 3 if x.even? result = "even" end local_variables # => [:x, :result]
Ruby says that both
result are defined, just like before.
But how can this be?! We know that no assignment to
result ever took place.
What we've run into here is a very special rule in how Ruby evaluates code. In a nutshell, the rule says that at a given line in the code, if there is any possibility that a local variable will exist, then that variable is guaranteed to always exist. Even if Ruby has to quietly assign it a
nil value to ensure its existence.
In effect when Ruby sees a case where a local variable might be created, Ruby rewrites our code to behave like this:
x = 3 result = nil if x.even? result = "even" end
Here, the result is always assigned a
Then the value is optionally overridden if
x turns out to be an even number.
In effect, Ruby autovivifies the
This rule has some interesting implications. For instance, check out this code. We have a simple method which takes an object and just sends
#to_s to it.
Then we write
foo = stringify(foo).
Surely this can't be valid, right? We're assigning the result of the method to a new variable, but we're also using that variable as an input to the method!
And yet, when we execute this, it works just fine, and returns the string equivalent of
def stringify(o) o.to_s end foo = stringify(foo) # => ""
This is another example of Ruby's implicit rewriting rules in action. Ruby ensures that it's not possible for a variable to be both existent and nonexistent on the same line of code.
So what do these strange rules about assignment mean for our code? Well, not a lot really.
It does mean that there's no need to write code like this, where we provide an else clause to assign a variable the value
nil in case an
if evaluates to false.
Ruby will take care of this for us, so we can omit the else.
if x.even? result = "even" else result = nil end
Other than that, the main value of knowing about this autovivification rule is that it can save us from confusion. If you've ever run into case where a variable was set to
nil even though no code had run which should have set it, now you know how it happened.
Before I go, I'd like to thank Todd A. Jacobs for suggesting today's topic. Thanks Todd, and happy hacking!