According to §11.3.1.2.2 of the Draft ISO Specification,
CACHE[:some_key] ||= "Some String"
expands to
o = CACHE
*l = :some_key
v = o.[](*l)
w = "Some String"
x = v || w
l << x
o.[]=(*l)
x
Or, in the more general case
primary_expression[indexing_argument_list] ω= expression
(I am using ω
here to denote any operator, so it could be ||=
, +=
, *=
, >>=
, %=
,…)
Expands to:
o = primary_expression
*l = indexing_argument_list
v = o.[](*l)
w = expression
x = v ω w
l << x
o.[]=(*l)
x
So, according to the specification, []=
will always get called. But that is actually not the case in current implementations (I tested MRI, YARV, Rubinius, JRuby and IronRuby):
def (h = {}).[]=(k, v) p "Setting #{k} to #{v}"; super end
h[:key] ||= :value # => :value
# "Setting key to value"
h[:key] ||= :value # => :value
So, obviously either the specification is wrong or all five currently released implementations are wrong. And since the purpose of the specification is to describe the behavior of the existing implementations, it's obviously that the specification must be wrong.
In general, as a first approximation
a ||= b
expands to
a || a = b
However, there's all kinds of subleties involved, for example, whether or not a
is undefined, whether a
is a simple variable or a more complex expression like foo[bar]
or foo.bar
and so on.
See also some of the other instances of this same question, that have already been asked and answered here on StackOverflow (for example, this one). Also, the question has been discussed so many times on the ruby-talk mailinglist, that there are now discussion threads whose sole purpose it is to summarize the other discussion threads. (Although please note that that list is far from complete.)