zoukankan      html  css  js  c++  java
  • 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  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.

  • 相关阅读:
    Java异常的分类
    Java SE 6 新特性: Java DB 和 JDBC 4.0
    Java SE 6 新特性: 对脚本语言的支持
    面向对象开发方法优点
    RocketMQ之八:水平扩展及负载均衡详解
    Bluetooth 4.0之Android 解说
    iOS截取视频缩略图的两种方法
    Java NIO Buffer
    spark 启动job的流程分析
    C语言堆内存管理上出现的问题,内存泄露,野指针使用,非法释放指针
  • 原文地址:https://www.cnblogs.com/iwangzheng/p/6037984.html
Copyright © 2011-2022 走看看