Sami
Sami

Reputation: 2971

What does &. (ampersand dot) mean in Ruby?

I came across this line of ruby code. What does &. mean in this?

@object&.method

Upvotes: 296

Views: 137701

Answers (8)

Uzbekjon
Uzbekjon

Reputation: 11813

Note: Even though @Santosh gave a clear and full answer, I would like add some more background and add an important note regarding its use with non instance variables.


It is called "Safe Navigation Operator" (aka "Optional chaining operator", "Null-conditional operator", etc.). Matz seems to call it "lonely operator". It was introduced in Ruby 2.3. It sends a method to an object only if it is not nil.

Example:

# Call method `.profile` on `user` only if `user` is not `nil`
@user&.profile

# Equivalent to
unless @user.nil?
  @user.profile
end

"Edge case" with local variables:

Please note, above code uses instance variables. If you want to use safe navigation operator with local variables, you will have to check that your local variables are defined first.

# `user` local variable is not defined previous
user&.profile

# This code would throw the following error:
NameError: undefined local variable or method `user' for main:Object

To fix this issue, check if your local variable is defined first or set it to nil:

# Option 1: Check the variable is defined
if defined?(user)
  user&.profile
end

# Option 2: Define your local variable. Example, set it to nil
user = nil
user&.profile     # Works and does not throw any errors

Method background

Rails has try method that basically does the same. It uses send method internally to call a method. Matz suggested that it is slow and this should be a built-in language feature.

Many other programming languages have similar features: Objective C, Swift, Scala, CoffeeScript, etc. However, a common syntax is ?. (question dot). But, this syntax could not be adopted by Ruby. Because ? was allowed in method names and thus, ?. symbol sequence is already a valid Ruby code. For example:

2.even?.class  # => TrueClass

That's why Ruby community had to come up with different syntax. It was an active discussion and different options were considered (.?, ?, &&, etc.). Here is a list of some considerations:

u.?profile.?thumbnails
u\profile\thumbnails
u!profile!thumbnails
u ? .profile ? .thumbnails
u && .profile && .thumbnails

# And finally
u&.profile&.thumbnails

While choosing the syntax, developers looked at different edge cases and the discussion is quite useful to go through. If you want to go through all variants and nuance of the operator, please see this feature introduction discussion on official Ruby issue tracker.

Upvotes: 100

Idan Gozlan
Idan Gozlan

Reputation: 3203

For all of those who came here from Typescript, it's the same as the ? operator

Upvotes: 0

Santhosh
Santhosh

Reputation: 29094

It is called the Safe Navigation Operator. Introduced in Ruby 2.3.0, it lets you call methods on objects without worrying that the object may be nil(Avoiding an undefined method for nil:NilClass error), similar to the try method in Rails.

So you can write

@person&.spouse&.name

instead of

@person.spouse.name if @person && @person.spouse

From the Docs:

my_object.my_method

This sends the my_method message to my_object. Any object can be a receiver but depending on the method's visibility sending a message may raise a NoMethodError.

You may use &. to designate a receiver, then my_method is not invoked and the result is nil when the receiver is nil. In that case, the arguments of my_method are not evaluated.

Upvotes: 383

G.O.
G.O.

Reputation: 21

Here's a short-read (3 mins) I found on this - it is pretty good.

To add to the above, it acts like the try! method in Rails, not the try method.

Because it will raise a NoMethodError exception if the receiver is not nil and does not implement the tried method.

Example taken from the above article:

account = Account.new(owner: Object.new)

account&.owner&.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`

account.try(:owner).try(:address)
# => nil

account.try!(:owner).try!(:address)
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`

Upvotes: 1

J&#246;rg W Mittag
J&#246;rg W Mittag

Reputation: 369428

what does mean object&.an_attribute in ruby on rails?

I am new in Ruby on rails and I saw this kind of code but I don't understand it:

In Ruby, like in most mainstream programming languages, user code cannot modify the fundamental workings of the programming languages, nor can it change the programming language's syntax.

Since Ruby on Rails is just Ruby code, it should be immediately obvious that this cannot possibly have anything to do with Ruby on Rails.

Therefore, we need to look at Ruby for an explanation, not Ruby on Rails.

The safe navigation operator or safe navigator is specified in language/safe_navigator_spec.rb of the ruby/spec, in particular here:

context "when context is nil" do
  it "always returns nil" do
    eval("nil&.unknown").should == nil
    eval("[][10]&.unknown").should == nil
  end

  it "can be chained" do
    eval("nil&.one&.two&.three").should == nil
  end

  it "doesn't evaluate arguments" do
    obj = Object.new
    obj.should_not_receive(:m)
    eval("nil&.unknown(obj.m) { obj.m }")
  end
end

It is documented in the Calling Methods section of the Ruby Syntax documentation:

&., 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.

Upvotes: -1

Gupta
Gupta

Reputation: 10358

safe navigation operator (&.): tells Ruby to only call the next method if the receiver isn’t nil. Otherwise, the expression returns nil.


Practical In Action

Let’s construct a Roster object for a Sports team. The Roster will contain multiple Player objects.

class Roster
  attr_accessor :players
end

class Player
  attr_accessor :name, :position
  
  def initialize(name, position)
    @name = name
    @position = position
  end

end

With these two objects, we can create a roster for a 2-on-2 women’s basketball tournament:

moore = Player.new("Maya Moore", "Forward")
taurasi = Player.new("Diana Taurasi", "Guard")
tourney_roster1 = Roster.new
tourney_roster1.players = [moore, taurasi]

If we want to know the forward for our 2-on-2 team, we might find the name this way:

if tourney_roster1.players.first.position == "Forward"
  puts "Forward: #{tourney_roster1.players.first.name}"
end

But what if our opposing roster isn’t set correctly?

tourney_roster2 = Roster.new
if tourney_roster2.players.first.position == "Forward"
  puts "Forward: #{tourney_roster1.players.first.name}"
end

tourney_roster2 hasn’t yet been set with any players. The preceding code will raise a NoMethodError because tourney_roster2.players returns nil. We can add conditional statements to avoid this, but it makes our if statement verbose and unclear:

if tourney_roster2.players &&
   tourney_roster2.players.first &&
   tourney_roster2.players.first.position == "Forward"

Instead, we can use the safe navigation operator to avoid the NoMethodError:

if tourney_roster2.players&.first&.position == "Forward"
  puts "Forward: #{tourney_roster1.players.first.name}"
end

Thus,

 >> tourney_roster2.players&.first == nil
  #=> true
 >> tourney_roster2.players&.first&.position == nil
  #=> true

Some legitimate use cases: The safe navigation operator comes in handy when working with multiple objects, as shown here, and when chaining methods together.

Upvotes: 9

Thomas Deranek
Thomas Deranek

Reputation: 353

Be wary! Though the safe navigation operator is convenient it can also be easy to trick yourself into changing your logic with it. I recommend avoiding the use of it in flow control. Example:

str = nil

puts "Hello" if str.nil? || str.empty?
# The above line is different than the below line
puts "Hello" if str&.empty?

In the first example, str.nil? returns true and str.empty? is never called, causing the puts statement to be executed. In the second example however, str&.empty? returns nil which is falsey, and the puts statement is never executed.

Upvotes: 11

Umut ADALI
Umut ADALI

Reputation: 1196

it used for nil check, such as in kotlin and swift For example; with Object -> Swift and Kotlin

model = car?.model

this model can be nil(Swift) or null(Kotlin) if we have not defined the model value in car class. we use that ampersand instead of question mark in ruby

model = car&.model

if use car.model without ampersand and if model is nil the system cannot continue running.

Upvotes: 4

Related Questions