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?
Imagine you have an
account that has an
owner and you want to get the
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):
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.
We can rewrite the previous example using the safe navigation operator:
The syntax is a bit awkward but I guess we will have to deal with it because it does make the code more compact.
Let’s compare all three approaches in more detail.
account = Account.new(owner: nil) # account without an owneraccount.owner.address# => NoMethodError: undefined method `address' for nil:NilClassaccount && account.owner && account.owner.address# => nilaccount.try(:owner).try(:address)# => nilaccount&.owner&.address# => nil
No surprises so far. What if
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# => falseaccount.try(:owner).try(:address)# => nilaccount&.owner&.address# => undefined method `address' for false:FalseClass`
Here comes the first surprise - the
&. syntax only skips
nil but recognizes
false! 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
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)# => nilaccount&.owner&.address# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
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
account.try!(:owner).try!(:address)# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
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
Be careful when using the
&. operator and checking for
nil values. Consider the following example:
nil.nil?# => truenil?.nil?# => falsenil&.nil?# => nil
Array#dig and Hash#dig
#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)# oraddress = params[:account].fetch(:owner) .fetch(:address)
You can now simply use
Hash#dig and accomplish the same thing:
address = params.dig(:account, :owner, :address)
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.
- &#39;NSURL&#39; is not implicitly converti
- Error:Execution failed for task &#39;:app:tran
- [上架] iOS "app-specific password" 上架问题
- Python学习5_解决python 提示 SyntaxError: Missing p
- objective-c中@interafce后面的"()" - 铂金蛋蛋 - 开
- RubyMine 2016.3 EAP，对 Puppet 的更好支持
- 解决python3 UnicodeEncodeError: &#39;gbk&#
- PHP - 函数 & 检测函数/类/方法是否存在
- PHP - session&cookies
- PHP - 字符串&字符串操作