DEV Community

Cover image for Ruby: Glass half nil?
Laura Berge
Laura Berge

Posted on

Ruby: Glass half nil?

When getting started with Ruby, it's common to see the following error:

NoMethodError (undefined method `method_name_here' for nil:NilClass)

To a beginner, this may be intimidating, but don't fret. All this means is that whatever variable we're trying to call the given method on (method_name_here in this case) is actually equivalent to nil. There's a good chance we assigned that variable equal to an item that simply does not exist.

I had this happen during the first project I made in Ruby. I was scraping a lot of data from tables on a Spanish vocabulary website. When I iterated over the data and tried printing it to the console, I got the NoMethodError. I had to hunt down why one or more of the vocabulary words were coming up as nil. It turned out that a couple of the table cells in the website were blank. Knowing what the error meant was crucial to solving this bug efficiently. In order to understand this error, we need to know more about what nil actually means.

What is Nil?

Last week, I wrote a blog about how Ruby is a purely object-oriented language, and I mentioned that nil itself is an object. The nil object refers to nothing or a lack of value. In other languages such as JavaScript, we use the term null. When we create a variable, we can set it equal to nil if we don't want to assign a value right away.

answer = nil
count = 0

# some code that finds our answer and stops looking when found
until answer do
    count += 1
    if count == 5
        answer = count
    end
end

p answer #=> 5

The above code runs the until statement until 'answer' has been assigned the value of the count variable. That is because when answer = nil, the truthiness of 'answer' is false. We could assign answer equal to false and get the same result. However, nil is more specific in that when an item returns nil, we know that it has a lack of value, or that we actually didn't return anything at all.

Nil is an object and has a class

If we navigate to the terminal and use IRB, we can look at nil and NilClass more in-depth. The first thing to know is that nil is an object and has a unique object id. This object id will always be the same - either 4 or 8 depending on your system. Nil also has a class of NilClass. NilClass has only one instance - nil. This is the same for true and false in Ruby. TrueClass has one instance called 'true' and FalseClass has a single instance called 'false'. NilClass has both class and instance methods available. Feel free to explore some of these, but we do not need to know them all and probably won't ever use most of them.

irb(main):001:0> nil.object_id
=> 8
irb(main):002:0> nil.class
=> NilClass
irb(main):003:0> NilClass.methods
=> [:allocate, :superclass, :<=>, :<=, :>=, :==, :===, :included_modules, :include?, :name, :ancestors, :attr, :attr_reader, :attr_writer, :attr_accessor, :instance_methods, :public_instance_methods, :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set, :class_variable_defined?, :freeze, :inspect, :private_constant, :public_constant, :const_missing, :deprecate_constant, :include, :singleton_class?, :prepend, :module_exec, :module_eval, :class_eval, :remove_method, :<, :>, :undef_method, :class_exec, :method_defined?, :alias_method, :to_s, :private_class_method, :public_method_defined?, :private_method_defined?, :protected_method_defined?, :public_class_method, :autoload?, :instance_method, :public_instance_method, :define_method, :autoload, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_get, :instance_variable_set, :protected_methods, :instance_variables, :private_methods, :method, :public_method, :public_send, :singleton_method, :define_singleton_method, :extend, :to_enum, :enum_for, :=~, :!~, :eql?, :respond_to?, :object_id, :send, :display, :class, :nil?, :hash, :dup, :singleton_class, :clone, :then, :itself, :yield_self, :untaint, :taint, :tainted?, :trust, :untrust, :untrusted?, :singleton_methods, :frozen?, :methods, :public_methods, :equal?, :!, :instance_exec, :!=, :instance_eval, :__id__, :__send__]
irb(main):004:0> nil.methods
=> [:&, :rationalize, :inspect, :to_a, :to_s, :===, :to_f, :to_i, :=~, :to_h, :nil?, :to_r, :to_c, :|, :^, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_get, :instance_variable_set, :protected_methods, :instance_variables, :private_methods, :method, :public_method, :public_send, :singleton_method, :define_singleton_method, :extend, :to_enum, :enum_for, :<=>, :!~, :eql?, :respond_to?, :freeze, :object_id, :send, :display, :class, :hash, :dup, :singleton_class, :clone, :then, :itself, :yield_self, :untaint, :taint, :tainted?, :trust, :untrust, :untrusted?, :singleton_methods, :frozen?, :methods, :public_methods, :equal?, :!, :==, :instance_exec, :!=, :instance_eval, :__id__, :__send__]
irb(main):005:0> 

Let's explore a couple of these methods:

irb(main):005:0> nil.to_i
=> 0
irb(main):006:0> nil.to_a
=> []
irb(main):007:0> nil.to_s
=> ""
irb(main):008:0> nil.nil?
=> true
irb(main):009:0> NilClass.nil?
=> false

Nil can be converted to an integer of 0, an empty array, or an empty string. However, these values are not the same as nil nor are they even evaluated as falsy.

irb(main):010:0> 0.nil?
=> false
irb(main):011:0> [].nil?
=> false
irb(main):012:0> "".nil?
=> false
irb(main):013:0> !!0
=> true
irb(main):014:0> !![]
=> true
irb(main):015:0> !!""
=> true
irb(main):016:0> !!nil
=> false

Encountering Nil

So when might we get the value of nil if we don't explicitly assign it?

Let's say we have a hash that represents our shopping list. We have to visit multiple stores to get all our items and each store has one or more items that we need.

shopping_list = {
    :cub_foods => ["olive oil", "flour", "rice", "cereal", "soup"],
    :walgreens => ["tylenol", "toothpaste", "claritin"],
    :farmers_market => ["veggies", "fruit", "honey"],
    :target => ["pasta", "plates"]
}

Now, let's say we forgot to add the pet store to our list. We're at PetSmart and check our list:

shopping_list[:pet_store]
#=> nil

We get back nil since that key currently does not exist in our shopping_list hash. So let's add some values.

shopping_list[:pet_store] = ["dog food"]

shopping_list[:pet_store][0]
#=> "dog food"

shopping_list[:pet_store][1]
#=> nil

shopping_list[:pet_store][3] = "new toy"

shopping_list[:pet_store]
#=> ["dog food", nil, nil, "new toy"]

So we can see that nil is simply returned when a value doesn't exist.

Questions

Let me know in the comments below if you have any lingering questions, comments, or even complaints about nil. Happy coding!

Oldest comments (2)

Collapse
 
eclecticcoding profile image
Chuck

Great read as always Laura.

Collapse
 
lberge17 profile image
Laura Berge

Thanks, Chuck!