The Safe Navigation Operator (&.) in Ruby


The most interesting addition to Ruby 2.3.0 is the Safe Navigation Operator(&.). A similar operator has been present in C# and Groovy for a long time with a slightly different syntax - ?.. So what does it do?

Scenario

Imagine you have an account that has an owner and you want to get theowner’s address. If you want to be safe and not risk a Nil error you would write something like the following:

if account && account.owner && account.owner.address
...
end

This is really verbose and annoying to type. ActiveSupport includes the trymethod which has a similar behaviour (but with few key differences that will be discussed later):

if account.try(:owner).try(:address)
...
end

It accomplishes the same thing - it either returns the address or nil if some value along the chain is nil. The first example may also return false if for example the owner is set to false.

Using &.

We can rewrite the previous example using the safe navigation operator:

account&.owner&.address

The syntax is a bit awkward but I guess we will have to deal with it because it does make the code more compact.

More examples

Let’s compare all three approaches in more detail.

account = Account.new(owner: nil) # account without an owner

account.owner.address
# => NoMethodError: undefined method `address' for nil:NilClass

account && account.owner && account.owner.address
# => nil

account.try(:owner).try(:address)
# => nil

account&.owner&.address
# => nil

No surprises so far. What if owner is false (unlikely but not impossible in the exciting world of shitty code)?

account = Account.new(owner: false)

account.owner.address
# => NoMethodError: undefined method `address' for false:FalseClass `

account && account.owner && account.owner.address
# => false

account.try(:owner).try(:address)
# => nil

account&.owner&.address
# => undefined method `address' for false:FalseClass`

Here comes the first surprise - the &. syntax only skips nil but recognizesfalse! It is not exactly equivalent to the s1 && s1.s2 && s1.s2.s3 syntax.

What if the owner is present but doesn’t respond to address?

account = Account.new(owner: Object.new)

account.owner.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>

account && account.owner && account.owner.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`

account.try(:owner).try(:address)
# => nil

account&.owner&.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`

Oops, the try method doesn’t check if the receiver responds to the given symbol. This is why it’s always better to use the stricter version of try -try!:

account.try!(:owner).try!(:address)
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`

Pitfalls

As Joeri Samson pointed out in the comments, this section is actually wrong - I mistakenly used ?. instead of &.. But I still think that the last example is confusing and nil&.nil? should return true.

Be careful when using the &. operator and checking for nil values. Consider the following example:

nil.nil?
# => true

nil?.nil?
# => false

nil&.nil?
# => nil

Array#dig and Hash#dig

The #dig method is, in my opinion, the most useful feature in this version. No longer do we have to write abominations like the following:

address = params[:account].try(:[], :owner).try(:[], :address)

# or

address = params[:account].fetch(:owner) .fetch(:address)

You can now simply use Hash#dig and accomplish the same thing:

address = params.dig(:account, :owner, :address)

Final words

I really dislike dealing with nil values in dynamic languages (check my previous posts) and think the addition of the safe operator and the digmethods is really neat. Note that Ruby 2.3.0 is still not released and some things might change in the final version.



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值