Equivalent of .try() for a hash to avoid "undefined method" errors on nil? [duplicate]

In Rails we can do the following in case a value doesn't exist to avoid an error:

@myvar = @comment.try(:body)

What is the equivalent when I'm digging deep into a hash and don't want to get an error?

@myvar = session[:comments][@comment.id]["temp_value"] 
# [:comments] may or may not exist here

In the above case, session[:comments]try[@comment.id] doesn't work. What would?


You forgot to put a . before the try:

@myvar = session[:comments].try(:[], @comment.id)

since [] is the name of the method when you do [@comment.id].


The announcement of Ruby 2.3.0-preview1 includes an introduction of Safe navigation operator.

A safe navigation operator, which already exists in C#, Groovy, and Swift, is introduced to ease nil handling as obj&.foo. Array#dig and Hash#dig are also added.

This means as of 2.3 below code

account.try(:owner).try(:address)

can be rewritten to

account&.owner&.address

However, one should be careful that & is not a drop in replacement of #try. Take a look at this example:

> params = nil
nil
> params&.country
nil
> params = OpenStruct.new(country: "Australia")
#<OpenStruct country="Australia">
> params&.country
"Australia"
> params&.country&.name
NoMethodError: undefined method `name' for "Australia":String
from (pry):38:in `<main>'
> params.try(:country).try(:name)
nil

It is also including a similar sort of way: Array#dig and Hash#dig. So now this

city = params.fetch(:[], :country).try(:[], :state).try(:[], :city)

can be rewritten to

city = params.dig(:country, :state, :city)

Again, #dig is not replicating #try's behaviour. So be careful with returning values. If params[:country] returns, for example, an Integer, TypeError: Integer does not have #dig method will be raised.