0

I have some doubts regarding OR EQUALS (||=) operator in ruby. How does ruby interpreter implement it? Here is a sample of code:

class C
  def arr
    @num ||= []
  end
end

When we use OR EQUALS operator in this circumstances, the first call to this method initializes the variable and adds an element, that's fine. When a second call is made to arr, how does it know that array has one element in it..

ichigolas
  • 7,595
  • 27
  • 50
bmalets
  • 3,207
  • 7
  • 35
  • 64

2 Answers2

3

In Ruby, there are two values that are considered logical false. The first is the boolean value false, the other is nil. Anything which is non-nil and not explicitly false is true. The first time though the method, @num is nil, which is treated as false and the logical or portion of ||= needs to be evaluated and ends up assigning the empty array to @num. Since that's now non-nil, it equates to true. Since true || x is true no matter what x is, in future invocations Ruby short circuits the evaluation and doesn't do the assignment.

pjs
  • 18,696
  • 4
  • 27
  • 56
0

In general terms x ||= y is equivalent to x = x || y, it's just shorthand. It's implemented as the expanded form, same as &&=, += or -=.

Most programming languages, Ruby included, will stop executing a logical comparison statement like || on the first true value it encounters and return that. Likewise, it will halt on the first false value when using &&.

In general terms:

false || foo()

This will return false and not evaluate foo().

The pattern is best described as a "lazy initializer", that is the variable is defined only once, but only when it's actually used. This is in contrast to an "eager initializer" that will do it as early as possible, like inside the initialize method.

You'll see other versions of this pattern, like:

def arr
  @num ||= begin
    stuff = [ ]
    # ...
    stuff
  end
end

This handles cases where the initial value is not as trivial and may need some work to produce. Once again, it's only actually generated when the method is called for the first time.

How does Ruby know on the second pass to not initialize it again? Simple, by that point @num is already defined as something.

As a note, if you're using values like false that would evaluate as non-true, then ||= will trigger every time. The only two logically false values in Ruby are nil and false, so most of the time this isn't an issue.

You'll have to do this the long-form way if you need to handle false as well:

def arr
  return @num unless num.nil?

  @num = false
end

There was talk of adding an ||=-like operator that would only trigger on nil but I don't think that has been added to Ruby yet.

tadman
  • 208,517
  • 23
  • 234
  • 262