Intro

If you’ve been programming in Ruby for any amount of time, you’ve probably seen an error like this:

NoMethodError: undefined method 'methodname' for nil:NilClass

Understanding the Exception

The meaning is actually pretty straightforward if you think about it. We’re trying to call a method called “methodname” that is not defined for the object we’re calling it on. In this case, that object is nil:

a = nil
a.methodname()
#=> NoMethodError: undefined method `methodname' for nil:NilClass

However, nil isn’t special here, other classes do throw the same error:

a = "string"
a.methodname()
#=> NoMethodError: undefined method `methodname' for "string":String

We’re only really focusing on the case where an undefined method is called on nil though. I just thought it was important to point out.

Anyways, probably the most common method you’ll see this happen with is: []

NoMethodError: undefined method `[]' for nil:NilClass

Problematic Example

You might see something like this when you’re pulling data from a network socket:

loop do
  data = @socket.gets(1024)
  puts data.unpack('A*')
end

In the above code, we assumed that every time we call gets, it’s going to return a String. When the server closes the socket and we gets again, we’re going to get a nil, and unpack is going to throw NoMethodError:

#=> NoMethodError: undefined method `unpack' for nil:NilClass

A Fix for Our Example

Let’s fix it:

loop do
  data = @socket.gets(1024)
  if data != nil
    puts data.unpack('A*')
  end
end

Well, that’s pretty tedious, there must be a better way…

Enter, the safe navigation operator!: &

A Better Fix: Safe Navigation Operator

loop do
  data = @socket.gets(1024)
  puts data&.unpack('A*')
end

That’s it, did you even notice? All we have to do is put a & between our possibly-nil object, and the method being called on it. If the object is nil, the method won’t be called, and nil will be returned instead of an exception.

So much easier than checking every single item manually!

Thoughts for the reader:

  • Is there a better way to handle this?
  • Where else might you run into errors like this?