What does the following code mean in Ruby?
||=
Does it have any meaning or reason for the syntax?
a ||= b
Signifies if any value is present in 'a' and you dont want to alter it the keep using that value, else if 'a' doesnt have any value, use value of 'b'.
Simple words, if left hand side if not null, point to existing value, else point to value at right side.
a ||= b
is the same as saying a = b if a.nil?
or a = b unless a
But do all 3 options show the same performance? With Ruby 2.5.1 this
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
takes 0.099 Seconds on my PC, while
1000000.times do
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
end
takes 0.062 Seconds. That's almost 40% faster.
and then we also have:
1000000.times do
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
end
which takes 0.166 Seconds.
Not that this will make a significant performance impact in general, but if you do need that last bit of optimization, then consider this result.
By the way: a = 1 unless a
is easier to read for the novice, it is self-explanatory.
Note 1: reason for repeating the assignment line multiple times is to reduce the overhead of the loop on the time measured.
Note 2: The results are similar if I do a=nil
nil before each assignment.
b = 5
a ||= b
This translates to:
a = a || b
which will be
a = nil || 5
so finally
a = 5
Now if you call this again:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
Now if you call this again:
a ||= b
a = a || b
a = 5 || 6
a = 5
If you observe, b
value will not be assigned to a
. a
will still have 5
.
Its a Memoization Pattern that is being used in Ruby to speed up accessors.
def users
@users ||= User.all
end
This basically translates to:
@users = @users || User.all
So you will make a call to database for the first time you call this method.
Future calls to this method will just return the value of @users
instance variable.
||=
is called a conditional assignment operator.
It basically works as =
but with the exception that if a variable has already been assigned it will do nothing.
First example:
x ||= 10
Second example:
x = 20
x ||= 10
In the first example x
is now equal to 10. However, in the second example x
is already defined as 20. So the conditional operator has no effect. x
is still 20 after running x ||= 10
.
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1
Because a
was already set to 1
irb(main):003:0> a = nil
=> nil
irb(main):004:0> a ||= 2
=> 2
Because a
was nil
This question has been discussed so often on the Ruby mailing-lists and Ruby blogs that there are now even threads on the Ruby mailing-list whose only purpose is to collect links to all the other threads on the Ruby mailing-list that discuss this issue.
Here's one: The definitive list of ||= (OR Equal) threads and pages
If you really want to know what is going on, take a look at Section 11.4.2.3 "Abbreviated assignments" of the Ruby Language Draft Specification.
As a first approximation,
a ||= b
is equivalent to
a || a = b
and not equivalent to
a = a || b
However, that is only a first approximation, especially if a
is undefined. The semantics also differ depending on whether it is a simple variable assignment, a method assignment or an indexing assignment:
a ||= b
a.c ||= b
a[c] ||= b
are all treated differently.