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:
Safe navigation operator
&.
- It is safe navigation operator which was introduced in Ruby 2.3.0. It basically returnsnil
if the callee isnil
instead of raising excecptionundefined 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 nilYou can read about it more here and documentation
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 blockmultiply_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 definedSymbol#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) }
endSo
&: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 isnil
. It returnsnil
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
Homebrew Installation on MAC Os X Failed to Connect to Raw.Githubusercontent.Com Port 443
Get All Instance Variables Declared in Class
How to Traverse Symlinked Directories in Ruby with a "**" Glob
How to Get Past "Http://Gems.Rubyforge.Org/ Does Not Appear to Be a Repository" Error Message
To_Specs': Could Not Find Chef (>= 0) Amongst [] (Gem::Loaderror)
Ruby Regex Error: Incompatible Encoding Regexp Match (Ascii-8Bit Regexp with Utf-8 String)
In Ruby, Can You Perform String Interpolation on Data Read from a File
Activerecord::Connectiontimeouterror
Show Full Path Name of the Ruby File When It Get Loaded
Difference Between Downcase and Downcase! in Ruby
How to Easily Parse a Url with Parameters in a Rails Test
Doing a Http Basic Authentication in Rails
Why Can't I Access a Local Variable Inside a Method in Ruby
How to Find Out Who Is Connected to Actioncable
Ruby Forgets Local Variables During a While Loop