As Ruby 2.3 introduces the Safe navigation operator( &.
), aka 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?
foo&.bar
is shorthand for foo && foo.bar
, so what would you expect the result of the expression nil && nil.nil?
to be?
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.
safe navigation operator:
tells Ruby to only call the next method if the receiver isn't nil. Otherwise, the expression returns nil.
class Roster attr_accessor:players end
class Player
attr_accessor :name, :position
def initialize(name, position)
@name = name
@position = position
end
end
With these two objects, we can create a roster for a 2-on-2 women's basketball tournament:
moore = Player.new("Maya Moore", "Forward")
taurasi = Player.new("Diana Taurasi", "Guard")
tourney_roster1 = Roster.new
tourney_roster1.players = [moore, taurasi]
If we want to know the forward for our 2-on-2 team, we might find the name this way:
if tourney_roster1.players.first.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
But what if our opposing roster isn't set correctly?
tourney_roster2 = Roster.new
if tourney_roster2.players.first.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
tourney_roster2 hasn't yet been set with any players. The preceding code will raise a NoMethodError because tourney_roster2.players returns nil. We can add conditional statements to avoid this, but it makes our if statement verbose and unclear:
if tourney_roster2.players &&
tourney_roster2.players.first &&
tourney_roster2.players.first.position == "Forward"
Instead, we can use the safe navigation operator to avoid the NoMethodError:
if tourney_roster2.players&.first&.position == "Forward"
puts "Forward: #{tourney_roster1.players.first.name}"
end
Some legitimate use cases: The safe navigation operator comes in handy when working with multiple objects, as shown here, and when chaining methods together.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.