Ruby - Safe Navigation Operator
- Intro
- Understanding the Exception
- Problematic Example
- A Fix for Our Example
- A Better Fix: Safe Navigation Operator
- Thoughts for the reader:
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:NilClassUnderstanding 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:NilClassHowever, nil isn’t special here, other classes do throw the same error:
a = "string"
a.methodname()
#=> NoMethodError: undefined method `methodname' for "string":StringWe’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:NilClassProblematic 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*')
endIn 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:NilClassA Fix for Our Example
Let’s fix it:
loop do
data = @socket.gets(1024)
if data != nil
puts data.unpack('A*')
end
endWell, 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*')
endThat’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?