26
votes

As Ruby 2.3 introduces the Safe navigation operator(&.), a.k.a lonely operator, the behavior on nil object seems odd.

nil.nil?    # => true
nil&.nil?   # => nil

Is that designed to behave like this way? Or some edge case that slipped away when adding the lonely operator?

2
Not clear why it seems odd to you. - sawa
Why is this better than using try in Rails? - Jwan622
@Jwan622 I think this is inspired by try. - Franklin Yu
@sawa It's odd because asking for nil? on something that is obviously nil (like the result of nil&.) should logically return true, just like nil.nil? does - Magne
@sawa The gotcha is that you're not actually calling nil? on the result of nil&., but it's rather a part of the initial expression nil && nil.nil?, which is ignored in this evaluation, since the first part of that expression is nil and thus returns. See Oeste's answer. - Magne

2 Answers

34
votes

foo&.bar is shorthand for foo && foo.bar, so what would you expect the result of the expression nil && nil.nil? to be?

8
votes

This is because nil&.nil? is shorthand for nil && nil.nil?. This would evaluate to nil && true, which is then nil.

(nil && x).nil? always evaluates to true, for any value of x.

While the syntax has power, this specific case has some potential to become a 'gotcha' for a developer:

(stuff&.things).nil? => This produces true if stuff doesn't exist, or stuff.things returns nil.

vs. the below case:

stuff&.things&.nil? => This produces nil in every case except the case where stuff.things returns something other than nil, in which case it would return false.

Because of the difficulty in normal boolean logic of differentiating between false and nil, it is unlikely that this would make sense in normal logic.