I was having a chat with a friend about a piece of Ruby code & the topic of return values came up…
…and of course, nil
is always part of that conversation.
Later that day, I was in bed about to go sleep, thinking, why do we use nil
?
I thought it would be a good idea to write about this the next day, and here I’m. I hope you find this interesting & learn something new!
On Return Values & Expected Behavior
When you call a method like select
you are always going to get an array.
Even if there is nothing to be selected.
Example:
[1,2,3].select {} # [] [1,2,3].select(&:even?) # [2]
But why is this important?
Good question!
If you always get an array you can call array methods (size
, empty?
, push
…) without having to check if you are actually working with an array.
That’s a good thing because class checking leads to high coupling.
If you want to write more flexible code avoid checking for “what class is this” as much as possible.
Returning Single Values
Ok, but what about using a method like find
, which only returns one value?
If the element is found it just works:
[1,2,3].find { |n| n == 2 } # 2
If there is no match, find
has to return something, it could return false
, but if we are expecting to call a method on this return value we are going to get an exception.
Here’s where nil
comes in, we use it because it’s the closest thing we can get to a generic Null Object.
As you may recall from my last article on nil, I showed you how nil
is a class with methods like to_i
, to_s
& to_a
.
This allows you to use methods like Array()
, and String()
to convert an object into these types.
Example:
String(123) # "123" String(nil) # "" Array(nil) # []
This also comes up in string interpolation:
"abc#{nil}dfg"
String interpolation calls to_s
on the result of evaluating what’s inside the interpolation block. So if you have #{1 + 1}
this converts into 2.to_s
.
Now the problem with nil
comes when you don’t expect it, you think it’s some object that responds to the foo
method, but it’s not, so you get the dreaded NoMethodError
exception.
In that case, you want to implement the Null Object pattern, as I describe in this article. Other solutions include using the fetch
method with a default value & raising an exception when a default value doesn’t make sense.
Summary
You’ve learned why we use nil
, how to avoid exceptions coming from calling methods on a nil
value, and how to write more confident code.
Have a look at your code & see if you can spot some type-checking code (is_a?
/ kind_of?
/ ===
). How can you improve that code by using what you learned today?