Safe Navigation Operator (&.) for Nil

Safe navigation operator (&.) for nil

foo&.bar is shorthand for foo && foo.bar, so what would you expect the result of the expression nil && nil.nil? to be?

Safe navigation operator (lonely operator) not working for hash

Because this

my_hash[:test]

is syntactic sugar for this one

my_hash.[](:test)

so this should work

my_hash&.[](:test) 

but it's not pretty, I know.

How does the &.method syntax (safe navigation operator) works in Ruby?

There are 2 seperate operators here:

  1. Safe navigation operator &. - It is safe navigation operator which was introduced in Ruby 2.3.0. It basically returns nil if the callee is nil instead of raising excecption undefined method called for Nil class. eg:

    a = 1
    a.next
    # => 2
    a&.next
    # => 2
    a = nil
    a.next
    # => NoMethodError (undefined method `next' for nil:NilClass)
    a&.next
    # => nil ## No exception, returns nil

    You can read about it more here and documentation

  2. Unary & : This operator is a little more complex. It is almost equivalent to calling #to_proc but not quite that. But for this discussion let us think like that. So, if you have a Proc, calling with & in front of it will call #to_proc on the Proc and convert it into a block

    multiply_by_2 = Proc.new { |x| x * 2 }
    # => #<Proc:0x00007fb4771cf560>
    # &multiply_by_2 almost equivalent to { |x| x * 2 } but is not correct syntax
    [1, 2].map(&multiply_by_2)
    # => [2, 4]
    # equivalent to [1, 2].map { |x| x * 2 }

    But what happens if we give a symbol like :abc to & operator instead of a proc. It will try to call #to_proc on the symbol and ruby has defined Symbol#to_proc which roughly translates to something like this:

    def to_proc
    # this will return some block like { |x| x.send(:abc) }
    lambda { |x| x.send(self) }
    end

    So &:abc roughly translates to this block { |x| x.abc } using the below transformation

    &:abc =====> :abc.to_proc =====> { |x| x.send(:abc) } ====> { |x| x.abc }

    So, instead of doing [1, 2, 3].map { |x| x.next }, you could do [1, 2, 3].map(&:next) as &:next is roughly equivalent to the block { |x| x.next }.

    See unary & (which is the main source of what I have written here) for more reading.

Does Ruby safe navigation operator evaluate its parameters when its receiver is nil?

To quote from the syntax documentation for the safe navigation operator:

&., called “safe navigation operator”, allows to skip method call when receiver is nil. It returns nil and doesn't evaluate method's arguments if the call is skipped.

As such, the arguments of your log method are not evaluated if the logger is nil when you call it as

logger&.log("something happened at #{Time.now}")

With that being said, note that the Ruby core logger offers a different solution to your exact issue, namely to avoid having to evaluate potentially expensive arguments if the log level is to high.

The Ruby core logger implements its add method something like this (simplified):

class Logger
attr_accessor :level

def initialize(level)
@level = level.to_i
end

def add(severity, message = nil)
return unless severity >= level

message ||= yield
log_device.write(message)
end

def info(message = nil, &block)
add(1, message, &block)
end
end

You can then use this as

logger = Logger.new(1)
logger.info { "something happened at #{Time.now}" }

Here, the block is only evaluated if the log level is high enough that the message is actually used.

Using [] with the Safe Navigation Operator in Ruby

Just use the ordinary (non-sugar) form.

request.path.match(/\A\/(?<slug>(?!admin|assets)\w+)/)&.[](:slug)

What is the difference between `try` and `&.` (safe navigation operator) in Ruby

&. works like #try!, not #try.

And here is description of #try! (from documentation):

Same as #try, but will raise a NoMethodError exception if the receiving is not nil and does not implemented the tried method.

So basically it saves you from calling a method on nil, but if an object is presented it will try to call its method as usual.

The quote is from Rails Documentation, and so it's important to emphasize
that Ruby does not provide #try; it's provided by Rails, or more accurately ActiveSupport. The safe navigation operator (&.) however, is a language feature presented in Ruby 2.3.0.

Ruby: Safe-navigation operator, undefined method `call`

This works fine in Ruby 2.3+ :

unreliable&.> 10

For example :

[-5, 0, nil, 5].each do |unreliable|
p unreliable&.> 0
end
# false
# false
# nil
# true

The way you tried it, Ruby expects unreliable to be a callable object such as a Proc :

unreliable = Proc.new{ |*params| puts "unreliable has been called with #{params}" }
unreliable&.(:>, 10)
# unreliable has been called with [:>, 10]
unreliable.call(:>, 10)
# unreliable has been called with [:>, 10]
unreliable&.call(:>, 10)
# unreliable has been called with [:>, 10]
unreliable[:>, 10]
# unreliable has been called with [:>, 10]

With the safe-navigation operator, there's no need to put parens and the method should be a method name, not a symbol (Rails' try expects a symbol).



Related Topics



Leave a reply



Submit