Reputation: 7810
I have an object that embeds an Array in its state. I am using deep_dup
on the object instance but the returned object references the same array object as the original instance, which is not what I was expecting.
Here is what I do:
class A
def initialize(arr)
@arr = arr.deep_dup
end
attr_accessor :arr
end
a = A.new(['a', 'b', 'c'])
# => #<A:0x007fe6eb7ae458 @arr=["a", "b", "c"]>
b = a.deep_dup
# => #<A:0x007fe6f382ad20 @arr=["a", "b", "c"]>
b.arr[1] = 'e'
# => "e"
a
# => #<A:0x007fe6eb7ae458 @arr=["a", "e", "c"]>
b
# => #<A:0x007fe6f382ad20 @arr=["a", "e", "c"]>
a.arr.object_id
# => 70314884952600
b.arr.object_id
# => 70314884952600
I have read in the documentation of deep_dup
, that
The deep_dup method returns a deep copy of a given object.
What am I doing or understanding wrong?
Upvotes: 2
Views: 2386
Reputation: 11035
If you take a look at the source for deep_dup
, on Object
def deep_dup duplicable? ? dup : self end
it just calls dup
(duplicable?
is always true, by default). Where, for arrays it calls deep_dup
on every element:
def deep_dup map(&:deep_dup) end
Looking at dup
for Object
(since that's what ends up being called on A
):
Produces a shallow copy of obj—the instance variables of obj are copied, but not the objects they reference. dup copies the tainted state of obj.
So, to get your example to work, you'd need something like:
class A
attr_accessor :array
def initialize(array)
@array = array.deep_dup
end
def initialize_copy(copy)
copy.array = self.array.deep_dup
super
end
end
a = A.new(['a', 'b', 'c'])
b = a.deep_dup
b.array[1] = 'e'
puts a.inspect # => #<A:0x007f857be2f510 @array=["a", "b", "c"]>
puts b.inspect # => #<A:0x007f857be2f448 @array=["a", "e", "c"]>
puts a.array.object_id # => 70105642924 560 (spaces added for clarity)
puts b.array.object_id # => 70105642924 660
The initialize_copy
method isn't well documented anywhere, but the dup
method states
This method may have class-specific behavior. If so, that behavior will be documented under the #initialize_copy method of the class.
So it's set up as a way to customize the dup
process without actually overriding dup
and this documentation (which is the only real documentation I can find on that method) shows that it only takes the 1 parameter, which is the copy
Upvotes: 3