In my current work, I’m using (abusing) of the ruby safe operator &
, which is a
native implementation of the null pattern,
for example, in a chain of messages:
user.billings.first.cost
It could be thrown an error when for example the billings
of a user is empty and
first
returns a nil object and then nil.cost
creates an Exception.
With the safe operator &
this avoids the error:
user.billings.first&.cost # => 5.3838
So, in the last part of the code using the &
is not causing an error because the
safe operator logic is:
nil&.any_method # => nil
At the internals, the Ruby language is added with metaprogramming any method
into the class NilClass
and that method only returns nil
, this is totally
useful to avoid runtime errors.
The problem appears when is needed to compare values, specifically with the
methods ending with ?
, check this table:
nil.nil? # returns: true
nil&.nil? # returns: nil
nil.empty? # returns: true
nil&.empty? # returns: nil
nil.blank? # returns: true
nil&.blank? # returns: nil
nil.present? # returns: false
nil&.present? # returns: nil
And evaluating nil
in conditionals is false
, for example, this trivial code:
if user.store.purchases.empty?
send_email_suggesting_products
end
If the user does not have a store, nil.purchases
throws an exception, avoiding
the error the code looks like:
if user.store&.purchases&.empty?
send_email_suggesting_products
end
This code avoids runtime errors but adds a new logic error, this code never
sent an email, that is because nil&.empty?
always returns nil
if the store
is a nil
value.
To avoid this behavior only delete the &
at the end, now the code is:
if user.store&.purchases.empty?
send_email_suggesting_products
end
now nil.empty?
(in rails) returns the correct true
and sends the email.
Conclusion
Use safe operator &
with confidence, but when you add to a method that compares
values, avoid or double-check the usage of &
.