I'm looking at some techniques to deep-copy objects in Ruby (MRI 1.9.3).
I came across the following example, but I'm not sure about the #dup
method implementation.
I tested it and it does work, but I don't understand the logical steps of the method, thus I'm not confortable using it in my own code.
Is the statement @name = @name.dup
referring to the iVar inside the copy? How? I can't see it.
Could anyone explain it, please?
Also, are there better ways?
class MyClass
attr_accessor :name
def initialize(arg_str) # called on MyClass.new("string")
@name = arg_str # initialize an instance variable
end
def dup
the_copy = super # shallow copy calling Object.dup
@name = @name.dup # new copy of istance variable
return the_copy # return the copied object
end
end
This is a really thin, very specific implementation of a "deep copy". What it's demonstrating is creating an independent @name
instance variable in the clone so that modifying the name of one with an in-place operation won't have the side-effect of changing the clone.
Normally deep-copy operations are important for things like nested arrays or hashes, but they're also applicable to any object with attributes that refer to things of that sort.
In your case, to make an object with a more robust dup
method, you should call dup
on each of the attributes in question, but I think this example is a bit broken. What it does is replace the @name
in the original with a copy, which may break any references you have.
A better version is:
def dup
copy = super
copy.make_independent!
copy
end
def make_independent!
instance_variables.each do |var|
value = instance_variable_get(var)
if (value.respond_to?(:dup))
instance_variable_set(var, value.dup)
end
end
end
This should have the effect of duplicating any instance variables which support the dup
method. This skips things like numbers, booleans, and nil
which can't be duplicated.
When you do super
, the instance method @name
will be shared between the original and the duplicate. By doing @name = @name.dup
, The original gets a new instance of a string as @name
, ending up having a different @name
from the one that the duplicated instance has.