What does the following code mean in Ruby?
||=
Does it have any meaning or reason for the syntax?
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.