Which method to define on a Ruby class to provide dup / clone for its instances?
Asked Answered
C

1

14

I have a Pointer class with a single attribute :contents, that points to an object of class MyObject.

class MyObject
  def hello; "hello" end
end

class Pointer
  attr_reader :contents
  def initialize( cont ); @contents = cont end
  # perhaps define some more state
end

I want my Pointer to be able to make copies of itself. I know that #dup method is defined by default, while #clone method is expected to be overriden to be able to make deep copies. But here, the copies don't have to be too deep. So, the first dilemma that I have is, should I override #dup method, because I don't really want to copy the additional state of my Pointer, just make a new one pointing to the same MyObject instance? Or should I refrain from overridine #dup, because I am not "supposed to" and override #clone with a method making shallow copies?

I would welcome comments on the above, but let's say that I will choose to override #dup. I could do just this:

class Pointer
  def dup; self.class.new( contents ) end
end

But online, I read something like "the dup method will call the initialize copy method". Also, this guy writes about #initialize_clone, #initialize_dup and #initialize_copy in Ruby. That leaves me wondering, is the best practice perhaps like this?

class Pointer
  def initialize_copy
    # do I don't know what
  end
end

Or like this?

class Pointer
  def initialize_dup
    # do I don't know what
  end
end

Or should I just forget about online rants written to confuse beginners and go for overriding #dup without concerns?

Also, I do understand that I can just call #dup without defining any custom #dup, but what if I want to define #dup with different behavior?

Also, the same question apply to #clone - should I try to define #initialize_clone or just #clone?

Cotten answered 14/8, 2012 at 2:55 Comment(2)
May I ask why you're making a pointer class at all? Ruby's variables are all already references/pointers.Dizzy
Sometimes you want to implement your own data structure or you want the pointer to do tricks. The suject of the question is not whether the class is a pointer or something else, but if you must know, I am interested in Ted Nelson's Zz structures, and Ted defines what he calls "cursors", so I decided to call them "points" like in Emacs. You know, you have to implement the pointer class when the specification of the data structure calls for it.Cotten
M
23

From my experience, overloading #initialize_copy works just fine (never heard about initialize_dup and initialize_clone).

The original initialize_copy (which initializes every instance variable with the values from the original object) is available through super, so I usually do:

class MyClass
  def initialize_copy(orig)
    super
    # Do custom initialization for self
  end
end
Mccutchen answered 14/8, 2012 at 13:0 Comment(5)
How does it compare with redefining #dup? Is it ever o.k. to redefine #dup?Cotten
The big difference is that, when you redefine #dup, 'self' is the original object. You therefore don't have access to the new object's internal state. It is the other way around in #initialize_copyMccutchen
One more difference: I learned the hard way that, in Matz' Ruby implementation, some C functions call e.g. rb_obj_dup directly instead of using the normal method dispatch. #dup might be one of them. Which means that some standard library stuff would not call your version of #dup while it would call #initialize_copyMccutchen
Oh, and while I am at it, I had a look at the code in MRI. #initialize_dup and #initialize_clone do exist, but only on 1.9.Mccutchen
Thanks so much. Finally someone clarified this to me.Cotten

© 2022 - 2024 — McMap. All rights reserved.