Christian Sakai
Christian Sakai

Reputation: 979

Ruby conditional assignment with a hash

anyone care to explain what this syntax means in Ruby?

class Animal
  def name_category
    @animals ||={}
  end
end

Also, is there a way for setting up object variable without using attr_accessor, attr_reader, attr_writer or def initialize?

Upvotes: 0

Views: 1818

Answers (2)

jmromer
jmromer

Reputation: 2236

||= is Ruby's conditional assignment operator. a ||= b can usually be taken as short for

a || (a = b)

That is, a is assigned b if a is nil or false (i.e., a = b conditional on the falsiness of a).

The operator exploits a property of the way || is evaluated. Namely, that the right-hand operand of || is not evaluated unless the left-hand one is truthy (i.e., not false or nil). This is known as short-circuiting.

In your case, unless @animals already possesses a non-falsy value, it will be bound to an empty hash {}.

It's important to note that despite the superficial similarity of ||= to operators like += and -=, a ||= b is not equivalent to a = a || b.

For a counterexample, take a = Hash.new(true). Then:

a[:key] ||= :value
#=> true
a
#=> {}

Compare with

a[:key] = a[:key] || :value
#=> true

a
#=> {:key=>true}

However, strictly speaking a ||= b is also not equivalent to a || a = b. If a is undefined, then

>> a || a = false
#=> NameError: undefined local variable or method `a' for main:Object

but

>> a ||= false
#=> false

and

>> a = a || false
#=> false 

Something to keep in mind.

So it’s more accurate, if less syntactically elegant, to say that a ||= b is equivalent to

(defined?(a) && a) ? a : a = b

Here's a good post on Ruby Inside elaborating on this point further.

Upvotes: 6

ptyx
ptyx

Reputation: 4164

It's equivalent to (really long hand unpretty form):

if @animal == nil  # (falsey really, but nil is what we're looking for here)
  @animal = {}
end
return @animal

Basically: return @animal, unless it's not initialized in which case initialized it to {} before return it.

You could also write it as:

@animal = {} unless @animal
return @animal

Or

@animal ||= {}
return @animal

But then @animal ||= {} evaluates to @animal anyway, and you can skip the return

Upvotes: 0

Related Questions