简体   繁体   中英

Safe navigation operator (&.) for nil

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM