Search code examples
rubydeep-copy

Ruby: object deep copying


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

Solution

  • 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.