In Rubyists Already Use Monadic Patterns, Dave Fayram made a passing reference to using ||= to set a variable's value if its value were 'Nothing' (false or nil in Ruby). The resulting Reddit quickly picked up on his definition (which was fixed later) and argued about ||='s true meaning which isn't as obvious as many Rubyists think. This spread to Freenode's awesome #ruby-lang IRC channel where I picked it up.
A common misconception is that a ||= b is equivalent to a = a || b, but it behaves like a || a = b
In a = a || b, a is set to something by the statement on every run, whereas with a || a = b, a is only set if a is logically false (i.e. if it's nil or false) because || is 'short circuiting'. That is, if the left hand side of the || comparison is true, there's no need to check the right hand side.
a ||= b being equivalent to a = a || b is a popular interpretation for two reasons:
- If
aandbare both local variables,a = a || bis a short and natural reflection of the outcome. - Other operators like
+=and-=do operate this way (and this standard dates back to C), e.g.:a += bis equivalent toa = a + b
Do not confuse [op]= with anything related to ||= or &&=. They're entirely different ideas and are implemented entirely different[ly].
Evan Phoenix (of Rubinius fame)
What's happening then, if not a = a || b?
Here's a simple example of using a ||= b:
a = nil b = 20 a ||= b a # => 20
In this case, a ||= b seems to behave like a = a || b. As mentioned earlier, this is entirely due to a and b both being local variables.
Let's try something more complicated:
h = {}
def h.[]=(k, v)
puts "Setting hash key #{k} with #{v.inspect}"
super
end
# 1. The standard ||= approach
h[:x] ||= 10
h[:x] ||= 20
# 2. The a = a || b approach
h[:y] = h[:y] || 10
h[:y] = h[:y] || 20
# 3. The a || a = b approach
h[:z] || h[:z] = 10
h[:z] || h[:z] = 20
The output:
Setting hash key x with 10 Setting hash key y with 10 Setting hash key y with 10 Setting hash key z with 10
Note that in the first case, using ||=, the hash key's value is only set once. Once it becomes logically truthful (i.e. anything other than nil or false), h[:x] is no longer assigned any new values, not even itself.
The second case, using the a = a || b approach, does result in two assignments (of the same value). The value remains 10 but the syntax forces h[:y] to assign itself as a value again.
In the last case, the behavior is the same as in the first case, demonstrating that a || a = b is a more realistic notation.
Note: Exactly the same result occurs if we switch the hash for an array and the keys for integers.
Full Demonstration for Getter/Setter MethodsA similar outcome occurs if we're referring to objects with getter/setter methods (which you may call accessors):
class MyClass
attr_reader :val
def val=(val)
puts "Setting val to #{val.inspect}"
@val = val
end
end
# 1. The standard ||= approach
obj = MyClass.new
obj.val ||= 'a'
obj.val ||= 'b'
# 2. The a = a || b approach
obj = MyClass.new
obj.val = obj.val || 'c'
obj.val = obj.val || 'd'
# 3. The a || a = b approach
obj = MyClass.new
obj.val || obj.val = 'e'
obj.val || obj.val = 'f'
And the output shows off similar behavior to the hash and array example:
Setting val to "a" Setting val to "c" Setting val to "c" Setting val to "e"Default Hash Values: A Sneaky Edge Case?
Our travels don't end there though. Back in 2008, David Black noticed an edge case with hashes that have default values. If you follow the logic above to the letter, this case will not surprise you, although from a pragmatic point of view, it's curious.
Let's take a look:
hsh = Hash.new('default')
hsh[:x] # => 'default'
# 1. The standard ||= approach
hsh[:x] ||= 10
p hsh # => {}
# 2. The a = a || b approach
hsh[:y] = hsh[:y] || 10
p hsh # {:y=>"default"}
# 3. The a || a = b approach
hsh[:z] || hsh[:z] = 10
p hsh # {:y=>"default"}
Hashes with default values act in an.. interesting way, depending on your point of view. Merely accessing a value doesn't mean that the value is reified (made concrete) in the hash itself. The reason for this is that you can assign Procs to a hash's default_proc in order to perform calculations (or even to set values) when an unset key is accessed. It would be undesirable to avoid this behavior merely because a key was accessed earlier on.
Again, we note that the a || a = b-style approach gives the result closest to the reality of ||=.
describe "Conditional operator assignment 'obj.meth op= expr'" do
it "is equivalent to 'obj.meth op obj.meth = expr'" do
RubySpec's variables_spec file
Undefined Variables: Another Tricky CaseIn the comments for this post, Vikrant Chaudhary brought up another interesting case:
If a is not defined,
a || a = 42raises NameError, whilea ||= 42returns 42. So, they don't seem to be equivalent expressions.
Vikrant Chaudhary
It's lucky I said "behaves like" earlier - phew! But joking aside, Vikrant makes a good point.
This tricky case is a little like the hash case. Something intriguing about how Ruby operates behind the scenes throws a spanner into the works again. That is, a variable assignment, even if not run, immediately summons that variable into being. For example:
x = 10 if 2 == 5 puts x
Even though the first line won't be run, x will exist on the second line and no exception will be raised. Another nasty one:
x = x puts x
Whoa! Well, a ||= 42 is working in a similar way. Ruby sees the assignment at the parsing stage and creates the variable in a way that it wouldn't with a || a = 42, even though it ends up behaving like the latter once actual execution occurs.
This appears to have been a popular discussion point in Rubyland over the years, so I would be remiss not to include links to some of the best references:
- x ||= y, Redux by Rick DeNatale.
- The Reddit discussion about Dave Fayram's post
- comp.lang.ruby: Please explain nuances of ||=
- comp.lang.ruby: The definitive list of ||= (OR Equal) threads and pages
- rubyonrails-talk: What does "||=" mean?
- A short-circuit (||=) edge case
Today I've opened up tickets for the 3rd and 4th runs of my Ruby Reloaded online Ruby course. It's aimed at intermediate developers who want to dig deeper, boost their confidence, and bolster their Ruby skill set. We dig into test driven development (TDD), object oriented design, building a library from scratch, code style, and more. It takes place in November and December.
Check out RubyReloaded.com to learn more about the course, what it involves, and when it runs. There are 24 places on each and about half have gone to waiting list folks so far.
P.S. The coupon code INSIDE will give you a $50 discount to thank you for being a Ruby Inside reader!

Back in 2008 and 2009, Ruby Inside had a long line of "Interesting Ruby Tidbits That Don’t Need Separate Posts" posts, aimed at sharing a collection of news and libraries in one hit. In the last year, I've shifted Ruby Inside to focusing on less frequent tutorials or investigative features and have been putting all of the news on 