The Safe Navigation Operator (&.) in Ruby
2016-11-07 11:02
190 查看
The most interesting addition to Ruby 2.3.0 is the Safe Navigation Operator(
different syntax -
Imagine you have an
If you want to be safe and not risk a Nil error you would write something like the following:
This is really verbose and annoying to type. ActiveSupport includes the
later):
It accomplishes the same thing - it either returns the address or
The first example may also return
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.
No surprises so far. What if
in the exciting world of shitty code)?
Here comes the first surprise - the
It is not exactly equivalent to the
What if the owner is present but doesn’t respond to
Oops, the
As Joeri Samson pointed out in the comments, this section is actually wrong - I mistakenly used
But I still think that the last example is confusing and
Be careful when using the
Consider the following example:
The
You can now simply use
I really dislike dealing with
is really neat. Note that Ruby 2.3.0 is still not released and some things might change in the final version.
&.). 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 accountthat has an
ownerand you want to get the
owner’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
nilif some value along the chain is
nil.
The first example may also return
falseif for example the
owneris
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
owneris
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
nilbut recognizes
false!
It is not exactly equivalent to the
s1 && s1.s2 && s1.s2.s3syntax.
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
trymethod 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
nilvalues.
Consider the following example:
nil.nil? # => true nil?.nil? # => false nil&.nil? # => nil
Array#dig and Hash#dig
The #digmethod 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#digand accomplish the same thing:
address = params.dig(:account, :owner, :address)
Final words
I really dislike dealing with nilvalues 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.
相关文章推荐
- The Safe Navigation Operator (&.) in Ruby
- VSS出错"Could not find the Visual SourceSafe Internet Web Service connection..."
- 错误error C2665: 'operator new' : none of the 5 overloads could convert all the argument types
- [Java JVM] Hotspot GC研究- GC安全点 (Safepoint&Stop The World)
- vs .net 2005 打开VSS项目时出现:"could not find the visual sourcesafe internet web service connection"的解决方案
- Warning: phpinfo() [function.phpinfo]: It is not safe to rely on the system's timezone settings.
- VSS出错"Could not find the Visual SourceSafe Internet Web Service connection..."
- 错误error C2665: 'operator new' : none of the 5 overloads could convert all the argument types
- Warning: strtotime() [function.strtotime]: It is not safe to rely on the system&#39;s timezone sett
- What is the name of the “-->” operator?(Stackoverflow)
- warning C4366: The result of the unary '&' operator may be unaligned
- Misleading error message in PowerShell script: "Invalid assignment expression. The left hand side of an assignment operator need
- 解决PHP date调用警告:It is not safe to rely on the system's timezone settings
- PHP Warning: phpinfo(): It is not safe to rely on the system's timezone settings
- swift3 截取字符串(substringwithrange)报错,it will be removed in Swift 3. Use the '..<' operator.
- 'init(start:end:)' is deprecated: it will be removed in Swift 3. Use the '..<' operator
- VSS出错"Could not find the Visual SourceSafe Internet Web Service connection..."
- PHP Warning: date() [function.date]: It is not safe to rely on the system's timezone
- 问题解决:javax.servlet.jsp.el.ELException: The "." operator was supplied with an index value of type "java.lang.String" to be applie
- VSS提示"Could not find the Visual SourceSafe Internet Web Service...