What does #foo || #foo = "bar" mean in Ruby and why? - ruby

What does #foo || #foo = "bar" mean in Ruby? To me, it seems to have the meaning of #foo || (#foo = "bar") instead of (#foo || #foo) = "bar". This, however, contradicts with the fact that || has a higher precedence than = in Ruby.
I am aware that || in Ruby is a short-circuit operator. I also understand that || is to be evaluated before = according to precedence. What I don't understand is that #foo = "bar" seems to be the right operant of ||. According to the precedence table, the right operant of || should be #foo, and the expression should be equivalent to (#foo || #foo) = "bar", which is illegal.

Ruby uses Short-circuit evaluation, and so it evaluates the first argument to decide if it should continue with the second one.
In case of #foo || #foo = "bar", OR operation is performed in such a way that when first argument is found nil, second argument is evaluated which is assignment statement. IF first argument is found/truthy then it will ignore second argument(statement in this case)
Note, it does not just assign value to #foo, complete logical statement return value after assignment.
If you write, #foo = #foo || 'sd' logical operation are performed first & then assigned.

I think in the end it truly is simply Short-circuit-evaluation as ray mentions in his answer, but I think the documentation is lacking in this regard.
|| is a short-circuit operator so as evaluation proceeds left to right, as soon as it finds a true, it is done and will return true, otherwise it will proceed to the right
the modifier-if (also listed on the page you linked to) has lower precedence than the assignment =, however assignment is not a short-circuit operator, so code like
foo = "not set"
is_true = false
foo = 42 if is_true
puts foo
will print
not set
while code such as:
foo = "not set"
is_true = true
foo = 42 if is_true
puts foo
will print
42
|| is a short-circuit operator so code such as this:
foo = 0
foo || foo = 42
puts foo
will print
0
because the left hand foo is not nil, so there is no reason to evaluate the right hand side.
while code such as this:
foo = nil
foo || foo = 42
puts foo
will print
42
since foo is nil, evaluation will continue to the right side of the || until it his a something that is true or return the last expression which will be true or false.
I agree however that the document you point to would imply that
x || y = z should be based on precedence could be interpreted as (x || y) = z which would not be legal. However it's also obvious that this would be the same as trying to assign a value to a boolean.
For example:
(true || true) = 42
is a syntax error
as does:
a = nil
b = nil
(a || b) = 42
yet
a = nil
b = nil
a || b = 42
puts b
results in
42

Simply, your interpretation of the precedence rule is wrong.
Precedence rule applies when there is ambiguity. With assignment syntax, the left hand side of = has to be a single token that is legal as a constant or some type of variable.
#foo || #foo = ...
can be only interpreted as
#foo || (#foo = ...)
and there is no ambiguity here, hence no issue of precedence.
What the precedence rule is concerned with is the right hand side of =. It is saying that
#foo = bar || baz
is interpred as
#foo = (foo || baz)
and not
(#foo = foo) || baz

Related

When is a line of Ruby parsed? evaluated? executed?

I was a little surprised to discover that person is defined by the following line of code, even when params[:person_id] doesn't exist:
person = Person.find(params[:person_id]) if params[:person_id]
I kind of expected that Ruby would first check the if statement and only define person then. In practice it seems person is defined earlier than that but remains nil.
While investigating that I tried the following:
irb> foo
# NameError (undefined local variable or method `foo' for main:Object)
irb> if false
irb> foo = 'bar'
irb> end
irb> foo
# => nil
Initially foo is undefined. But then it gets defined, even though it's only referenced inside an if block that isn't evaluated.
I'm now guessing that the whole program gets parsed(?) and that a foo node is added to the Abstract Syntax Tree (i.e. defined). The program is then executed(?), but that particular line is skipped (not evaluated(?)) and so foo is nil (defined but not set to a value).
I'm not sure how to confirm or refute that hunch though. How does one go about learning and digging into the Ruby internals and finding out what happens in this particular scenario?
Answering my own question, Jay's answer to a similar question linked to a section of the docs where it is explained:
The local variable is created when the parser encounters the assignment, not when the assignment occurs
There is a deeper analysis of this in the Ruby Hacking Guide (no section links available, search or scroll to the "Local Variable Definitions" section):
By the way, it is defined when “it appears”, this means it is defined even though it was not assigned. The initial value of a defined [but not yet assigned] variable is nil.
That answers the initial question but not how to learn more.
Jay and simonwo both suggested Ruby Under a Microscope by Pat Shaughnessy which I am keen to read.
Additionally, the rest of the Ruby Hacking Guide covers a lot of detail and actually examines the underlying C code. The Objects and Parser chapters were particularly relevant to the original question about variable assignment (not so much the Variables and constants chapter, it simply refers you back to the Objects chapter).
I also found that a useful tool to see how the parser works is the Parser gem. Once it is installed (gem install parser) you can start to examine different bits of code to see what the parser is doing with them.
That gem also bundles the ruby-parse utility which lets you examine the way Ruby parses different snippets of code. The -E and -L options are most interesting to us and the -e option is necessary if we just want to process a fragment of Ruby such as foo = 'bar'. For example:
> ruby-parse -E -e "foo = 'bar'"
foo = 'bar'
^~~ tIDENTIFIER "foo" expr_cmdarg [0 <= cond] [0 <= cmdarg]
foo = 'bar'
^ tEQL "=" expr_beg [0 <= cond] [0 <= cmdarg]
foo = 'bar'
^~~~~ tSTRING "bar" expr_end [0 <= cond] [0 <= cmdarg]
foo = 'bar'
^ false "$eof" expr_end [0 <= cond] [0 <= cmdarg]
(lvasgn :foo
(str "bar"))
ruby-parse -L -e "foo = 'bar'"
s(:lvasgn, :foo,
s(:str, "bar"))
foo = 'bar'
~~~ name
~ operator
~~~~~~~~~~~ expression
s(:str, "bar")
foo = 'bar'
~ end
~ begin
~~~~~ expression
Both of the references linked to at the top highlight an edge case. The Ruby docs used the example p a if a = 0.zero? whlie the Ruby Hacking Guide used an equivalent example p(lvar) if lvar = true, both of which raise a NameError.
Sidenote: Remember = means assign, == means compare. The if foo = true construct in the edge case tells Ruby to check if the expression foo = true evaluates to true. In other words, it assigns the value true to foo and then checks if the result of that assignment is true (it will be). That's easily confused with the far more common if foo == true which simply checks whether foo compares equally to true. Because the two are so easily confused, Ruby will issue a warning if we use the assignment operator in a conditional: warning: found `= literal' in conditional, should be ==.
Using the ruby-parse utility let's compare the original example, foo = 'bar' if false, with that edge case, foo if foo = true:
> ruby-parse -L -e "foo = 'bar' if false"
s(:if,
s(:false),
s(:lvasgn, :foo,
s(:str, "bar")), nil)
foo = 'bar' if false
~~ keyword
~~~~~~~~~~~~~~~~~~~~ expression
s(:false)
foo = 'bar' if false
~~~~~ expression
s(:lvasgn, :foo,
s(:str, "bar"))
foo = 'bar' if false # Line 13
~~~ name # <-- `foo` is a name
~ operator
~~~~~~~~~~~ expression
s(:str, "bar")
foo = 'bar' if false
~ end
~ begin
~~~~~ expression
As you can see above on lines 13 and 14 of the output, in the original example foo is a name (that is, a variable).
> ruby-parse -L -e "foo if foo = true"
s(:if,
s(:lvasgn, :foo,
s(:true)),
s(:send, nil, :foo), nil)
foo if foo = true
~~ keyword
~~~~~~~~~~~~~~~~~ expression
s(:lvasgn, :foo,
s(:true))
foo if foo = true # Line 10
~~~ name # <-- `foo` is a name
~ operator
~~~~~~~~~~ expression
s(:true)
foo if foo = true
~~~~ expression
s(:send, nil, :foo)
foo if foo = true # Line 18
~~~ selector # <-- `foo` is a selector
~~~ expression
In the edge case example, the second foo is also a variable (lines 10 and 11), but when we look at lines 18 and 19 we see the first foo has been identified as a selector (that is, a method).
This shows that it is the parser that decides whether a thing is a method or a variable and that it parses the line in a different order to how it will later be evaluated.
Considering the edge case...
When the parser runs:
it first sees the whole line as a single expression
it then breaks it up into two expressions separated by the if keyword
the first expression foo starts with a lower case letter so it must be a method or a variable. It isn't an existing variable and it IS NOT followed by an assignment operator so the parser concludes it must be a method
the second expression foo = true is broken up as expression, operator, expression. Again, the expression foo also starts with a lower case letter so it must be a method or a variable. It isn't an existing variable but it IS followed by an assignment operator so the parser knows to add it to the list of local variables.
Later when the evaluator runs:
it will first assign true to foo
it will then execute the conditional and check whether the result of that assignment is true (in this case it is)
it will then call the foo method (which will raise a NameError, unless we handle it with method_missing).

Precedence of to-block unary `&`

Consider the following Ruby code:
[1,3].any? &:even? || true
# => false
[1,3].any? &nil || :even?
# => false
[1,3].any? &nil || :odd?
# => true
So it seems that Boolean-or || has higher precedence than to-proc unary &. I didn't expect this. Is that right, and is it documented anywhere?
This is what the (wrongly-maligned) and and or keywords are for. You're supposed to write that as
[1,3].any? &:even? or true
As for why this happens--I can't find documentation for this--but I think it actually has more to do with optional parentheses and the restrictions of unary &.
Unary & is special. "Normal" operators like ~ are essentially syntactic sugar over method calls; you can put them wherever you want. But & is only allowed in method arguments, and even then only at the end.
foo x, &bar
# NameError, determined at runtime because it has to see if any of these names are defined
foo &bar, x
# SyntaxError! Didn't even make it past the parser
y = bar
# NameError
y = &bar
# SyntaxError!
And when you leave parentheses out from a method call, it slurps up pretty much everything, stopping only at super-low-precedence stuff like if/unless/and/or.
foo bar baz if true
# same as
foo(bar(baz)) if true
So your example is equivalent to
[1,3].any?(&:even? || true)
Now if & were somehow high-precendence this is either a totally normal value to be evaluated at runtime true or it's a highly-restricted special syntactic construct &:even?. It's not great to discover syntax errors at runtime, so I guess the devs chose to solve it the easy way: make & super low precedence. That way the parser can just verify the syntax rules and ignore the block argument itself (which has to be evaluated at runtime).
When the & is not preceded by an object (unary) and is immediately followed by by anything else (&nil in this case). It will be parsed as an attempted to_proc method call otherwise it will be treated as a method call on the receiver e.g.
nil&nil
#=> false
Is equivalent to nil.&(nil)
So in your case ([1,3].any? &nil || :even?) it is being parsed as [1,3].any?(&(nil || :even?)) because & (to_proc method call) has a lower precedence than logical || and needs to know the result of nil || :even? to proceed.
However, operational and (&) requires a receiver (not unary) but does hold a higher precedence than the logical or (||) e.g. [1,3].any? &nil&nil || :even? which would evaluate to
[1,3].any?(&(nil.&(nil) || :even?)
#=> false
# Or
[1,3].any?(&(nil & nil || :even?)
#=> false
Where strangely enough it is clear that operational and (&) has a higher precedence than logical or (||)
Further examples
true & nil || :even?
#=> :even?
true & :even?
#=> true
[1,3].any? &true&true || :even?
#=> TypeError: wrong argument type TrueClass (expected Proc)
# Okay no problem
class TrueClass
def to_proc
->(*_) { true }
end
end
[1,3].any? &true&true || :even?
#=> true
[1,3].any? &true || :even?
#=> true
Even stranger is that the unary & allows for nil but essentially ignores it from block_given? but any other object that does not implement to_proc raises a TypeError
def no_block
block_given? ? yield('Yes') : 'No'
end
no_block &nil
#=> "No"
no_block &false
#=> TypeError: wrong argument type FalseClass (expected Proc)

Using || in arguments in Ruby?

I just saw this code:
method_name(ARGV.shift || "somefile.txt")
which basically should first evalute ARGV[0] and if it doesn't exist then read from "somefile.txt".
My question was, can the && operator be also used here, and in what situations?
Also, does this concept of "passing this or this argument" to a method in Ruby have a name?
The short answer, logical operators are going to return one of its operands based on if it's truthy or falsey. In practice, nearly everything in Ruby will evaluate to true except false or nil.
Examples:
expr1 || expr2 will return (expr1 if not falsey) or (expr2)
In other words, the first truthy value or the final operand,
expr1 && expr2 will return (expr1 if falsey) or (expr2).
In other words, the first falsey value or the final operand.
As for an actual use case, a similar example would be:
Using the && operator to check for a flag in ARGV then passing the file name.
method_name(ARGV.include?('-w') && "write_file.txt")
It should be noted that this is probably not a widely accepted practice. (see comments)
However, preferring a user supplied value over a default value, by using ||, in this manner would be.
If && is used, then the argument would be nil when there is no ARGV[0] and "somefile.txt" when there is ARGV[0]. Note that elements of ARGV, if any, would be strings, so there is no possibility of ARGV[0] being nil or false when there is an element passed.
Generally, || and && are called "(short circuit) disjunction" and "(short circuit) conjunction", respectively.
A typical use case of || is to provide a default value:
foo = potentially_falesy_value || default
A typical use case of && is to provide a value that depends on the truthness of another value:
foo = hash[:bar] && hash[:bar][:baz]
|| is using for providing default values. || returns first "true" value. "True" value -- value that is interpreted as true boolean value in ruby. So first "true" value in the chain will be as the result of the expression. && returns first "false" value. Complete analogy. But it does not have such graceful application.
Apart from the obvious Boolean operator functionality, && can be used like you could in some languages such as JavaScript:
a = cond1 && cond2 && value # a is now value if cond1 and cond2,
# else nil or false (depends on cond1 and cond2)
It's not very readable (IMHO) when assigning non-Boolean variables, but it works.
first evalute ARGV[0] and if it doesn't exist then read from "somefile.txt". My ques
You are correct. The ARGV.shift || "somefile.txt" expression will evaluate to ARGV.shift if it returns some non-falsy value, and "somefile.txt" otherwise. Some other examples:
puts nil || "foo" # => "foo"
puts "foo" || "bar" # => "foo"
puts "foo" || nil # => "foo"
puts "foo" || raise # => "foo"
# and doesn't blow up, because the 'raise'
# is never evaluated
can the && operator be also used here
Sure, but is arguably of less practical value. It might be clearer to use an if in that case:
puts foo && bar
# is the same as:
if foo
puts bar
end
# or:
puts bar if foo
does this concept of "passing this or this argument to a method in Ruby" has a name?
I'm not sure if it has any 'official' name, but I commonly see this pattern being called 'default value' or 'fallback value'.

Position of "if" condition

I thought that:
do_something if condition
were equivalent to
if condition
do_something
end
I found a code that does not respect this rule.
if !(defined? foo)
foo = default_value
end
Here, foo takes default_value.
foo = default_value if !(defined? foo)
Here, foo takes nil. In the former code, I think if is executed first, and should be equivalent to:
foo = (default_value if !(defined? foo))
Is there any way to set to default_value if the variable is not defined?
General answer :
Some several comments want to use the ||= operator... Which will not work if foo is nil :
foo ||= default_value
will return the default value, while foo is defined.
I insist on using "not defined?", which is not equal to nil.
The Ruby way is
foo ||= default_value
But, of course
if (defined? foo)
foo = default_value
end
and
foo = default_value if !(defined? foo)
are different. You're not comparing the same thing.
In one you compare (defined? foo) and the other you compare !(defined? foo)
I think what you're really after is the following
if !(defined? foo)
foo = default_value
end
The two pieces of code are equivalent syntactically, but are different from the point of view of parsing. You are partially right that, "if is executed first", but that is only regarding syntax. Within parsing, the parsing order follows the linear order of the tokens. In Ruby, when you have an assignment:
foo = ...
then foo is assigned nil even if that portion of code is not syntactically evaluated, and that affects the result of defined?.
In order to write inline without having that problem, the way I do is to use and, or, &&, or ||:
defined?(foo) or foo = default_value

What does ||= (or-equals) mean in Ruby?

What does the following code mean in Ruby?
||=
Does it have any meaning or reason for the syntax?
a ||= b is a conditional assignment operator. It means:
if a is undefined or falsey, then evaluate b and set a to the result.
Otherwise (if a is defined and evaluates to truthy), then b is not evaluated, and no assignment takes place.
For example:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
Confusingly, it looks similar to other assignment operators (such as +=), but behaves differently.
a += b translates to a = a + b
a ||= b roughly translates to a || a = b
It is a near-shorthand for a || a = b. The difference is that, when a is undefined, a || a = b would raise NameError, whereas a ||= b sets a to b. This distinction is unimportant if a and b are both local variables, but is significant if either is a getter/setter method of a class.
Further reading:
http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html
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.
Concise and complete answer
a ||= b
evaluates the same way as each of the following lines
a || a = b
a ? a : a = b
if a then a else a = b end
-
On the other hand,
a = a || b
evaluates the same way as each of the following lines
a = a ? a : b
if a then a = a else a = b end
-
Edit: As AJedi32 pointed out in the comments, this only holds true if: 1. a is a defined variable. 2. Evaluating a one time and two times does not result in a difference in program or system state.
In short, a||=b means: If a is undefined, nil or false, assign b to a. Otherwise, keep a intact.
Basically,
x ||= y means
if x has any value leave it alone and do not change the value, otherwise
set x to y
It means or-equals to. It checks to see if the value on the left is defined, then use that. If it's not, use the value on the right. You can use it in Rails to cache instance variables in models.
A quick Rails-based example, where we create a function to fetch the currently logged in user:
class User > ActiveRecord::Base
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
end
It checks to see if the #current_user instance variable is set. If it is, it will return it, thereby saving a database call. If it's not set however, we make the call and then set the #current_user variable to that. It's a really simple caching technique but is great for when you're fetching the same instance variable across the application multiple times.
To be precise, a ||= b means "if a is undefined or falsy (false or nil), set a to b and evaluate to (i.e. return) b, otherwise evaluate to a".
Others often try to illustrate this by saying that a ||= b is equivalent to a || a = b or a = a || b. These equivalencies can be helpful for understanding the concept, but be aware that they are not accurate under all conditions. Allow me to explain:
a ||= b ⇔ a || a = b?
The behavior of these statements differs when a is an undefined local variable. In that case, a ||= b will set a to b (and evaluate to b), whereas a || a = b will raise NameError: undefined local variable or method 'a' for main:Object.
a ||= b ⇔ a = a || b?
The equivalency of these statements are often assumed, since a similar equivalence is true for other abbreviated assignment operators (i.e. +=,-=,*=,/=,%=,**=,&=,|=,^=,<<=, and >>=). However, for ||= the behavior of these statements may differ when a= is a method on an object and a is truthy. In that case, a ||= b will do nothing (other than evaluate to a), whereas a = a || b will call a=(a) on a's receiver. As others have pointed out, this can make a difference when calling a=a has side effects, such as adding keys to a hash.
a ||= b ⇔ a = b unless a??
The behavior of these statements differs only in what they evaluate to when a is truthy. In that case, a = b unless a will evaluate to nil (though a will still not be set, as expected), whereas a ||= b will evaluate to a.
a ||= b ⇔ defined?(a) ? (a || a = b) : (a = b)????
Still no. These statements can differ when a method_missing method exists which returns a truthy value for a. In this case, a ||= b will evaluate to whatever method_missing returns, and not attempt to set a, whereas defined?(a) ? (a || a = b) : (a = b) will set a to b and evaluate to b.
Okay, okay, so what is a ||= b equivalent to? Is there a way to express this in Ruby?
Well, assuming that I'm not overlooking anything, I believe a ||= b is functionally equivalent to... (drumroll)
begin
a = nil if false
a || a = b
end
Hold on! Isn't that just the first example with a noop before it? Well, not quite. Remember how I said before that a ||= b is only not equivalent to a || a = b when a is an undefined local variable? Well, a = nil if false ensures that a is never undefined, even though that line is never executed. Local variables in Ruby are lexically scoped.
If X does NOT have a value, it will be assigned the value of Y. Else, it will preserve it's original value, 5 in this example:
irb(main):020:0> x = 5
=> 5
irb(main):021:0> y = 10
=> 10
irb(main):022:0> x ||= y
=> 5
# Now set x to nil.
irb(main):025:0> x = nil
=> nil
irb(main):026:0> x ||= y
=> 10
x ||= y
is
x || x = y
"if x is false or undefined, then x point to y"
||= is a conditional assignment operator
x ||= y
is equivalent to
x = x || y
or alternatively
if defined?(x) and x
x = x
else
x = y
end
unless x
x = y
end
unless x has a value (it's not nil or false), set it equal to y
is equivalent to
x ||= y
Suppose a = 2 and b = 3
THEN, a ||= b will be resulted to a's value i.e. 2.
As when a evaluates to some value not resulted to false or nil.. That's why it ll not evaluate b's value.
Now Suppose a = nil and b = 3.
Then a ||= b will be resulted to 3 i.e. b's value.
As it first try to evaluates a's value which resulted to nil.. so it evaluated b's value.
The best example used in ror app is :
#To get currently logged in iser
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
# Make current_user available in templates as a helper
helper_method :current_user
Where, User.find_by_id(session[:user_id]) is fired if and only if #current_user is not initialized before.
||= 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.
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 equivalent to
a || a = b
and not
a = a || b
because of the situation where you define a hash with a default (the hash will return the default for any undefined keys)
a = Hash.new(true) #Which is: {}
if you use:
a[10] ||= 10 #same as a[10] || a[10] = 10
a is still:
{}
but when you write it like so:
a[10] = a[10] || 10
a becomes:
{10 => true}
because you've assigned the value of itself at key 10, which defaults to true, so now the hash is defined for the key 10, rather than never performing the assignment in the first place.
It's like lazy instantiation.
If the variable is already defined it will take that value instead of creating the value again.
Please also remember that ||= isn't an atomic operation and so, it isn't thread safe. As rule of thumb, don't use it for class methods.
This is the default assignment notation
for example: x ||= 1
this will check to see if x is nil or not. If x is indeed nil it will then assign it that new value (1 in our example)
more explicit:
if x == nil
x = 1
end
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.
As a common misconception, a ||= b is not equivalent to a = a || b, but it behaves like a || a = b.
But here comes a tricky case. If a is not defined, a || a = 42 raises NameError, while a ||= 42 returns 42. So, they don't seem to be equivalent expressions.
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 ruby-lang syntax. The correct answer is to check the ruby-lang documentation. All other explanations obfuscate.
Google
"ruby-lang docs Abbreviated Assignment".
Ruby-lang docs
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
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.

Resources