"Everything in Ruby is an object". This is also true for nested hashes and arrays. If you copy a hash with #clone
or #dup
and you modify the copy, you will run into the following behavior:
original_hash = { foo: { bar: 'original value' } }
copied_hash = original_hash.dup
copied_hash[:foo][:bar] = 'changed value'
original_hash # => { foo: { bar: "changed value" }
This is, because { bar: 'baz' }
is an object, which is referenced in :foo
. The copy of original_hash
still holds the reference to the same object, so altering the value will also affect the copied hash.
This will get more obvious, if you are having references to e.g. ActiveRecord objects.
But in most cases, if you want to copy a hash, you want to get a copy of the whole structure. And this is where #deep_dup
comes into play:
original_hash = { foo: { bar: 'original value' } }
copied_hash = original_hash.deep_dup
copied_hash[:foo][:bar] = 'changed value'
original_hash # => { foo: { bar: "original value" }
How it works:
#deep_dup
simply calls itself recursively for arrays and hashes and calls #dup
for all other Objects where #duplicable?
is true.
Be careful using #deep_dup
Don't use #deep_dup
when you are not sure about cyclic dependencies, because you will run into an endless loop:
a = {}
b = {a: a}
a[:b] = b
a.deep_dup # SystemStackError: stack level too deep