In Ruby, everything is an object and all objects are passed around using pointers (there are exceptions, but they don't apply here). When you do
something = {}
another = something
a single Hash object is created and both variables point to that same object. When you do
another[x] ||= {}
a single Hash object is created and a pointer to that object is added to the Hash pointed to by another
. Another way to write that would be
old_hash = another
new_hash = {}
old_hash[x] = new_hash
another = new_hash
Remember: all of those assignments are just moving around pointers to objects. It should now be clear that another
is pointing to the new (empty) hash. However, since old_hash
also contains a pointer to the new hash, any futures changes to the new hash (i.e. another
) will be seen by old_hash
. That is why something
grows with each iteration.
Your example is really no different from this simple situation:
x = {}
y = {}
z = {}
x["a"] = y
y["b"] = z
z["c"] = {}
puts x
# {"a"=>{"b"=>{"c"=>{}}}}
Your example just reverses the last three assignments - which makes no difference to the result.