Just some thoughs and code snippets - no general solutions here...

"Paul Brannan" <pbrannan / atdesk.com> schrieb im Newsbeitrag
news:20040614142913.GV2788 / atdesk.com...
> Suppose I have an class that needs to implement its own dup/clone
> methods.  What is the correct way to write these methods?
>
> One way is to create protected accessors:
>
>   class Foo
>     def initialize(foo, bar)
>       @foo = foo
>       @bar = bar
>     end
>
>     def dup
>       copy = super
>       copy.foo = @foo.dup
>       copy.bar = @bar.dup
>     end

You need to return 'copy' here.

>     protected
>     attr_accessor :foo
>     attr_accessor :bar
>   end
>
> Another way is to use instance_eval:
>
>   class Foo
>     def dup
>       copy = super
>       foo = @foo.dup
>       bar = @bar.dup
>       copy.instance_eval do
>         @foo = foo
>         @bar = bar
>       end

You need to return 'copy' here.

>     end
>   end
>
> Questions:
>
> 1. How else might dup/clone be implemented?  (the deep-copy trick using
>    marshaling is one way, but the goal here is to write a one-level-deep
>    dup/clone).
>
> 2. What is the right way to set the new instance variables in the copy?

I guess it really depends...

> 3. Should dup be implemented in terms of clone or should clone be
>    implented in terms of dup (or should they both be implemented
>    independently or in terms of another function)?

IMHO neither since #dup and #clone do have different semantics with
respect to freeze.  Well, you could do something like this:

class Foo
  attr_accessor :foo, :bar

  def dup; do_copy( super, :dup ); end
  def clone; do_copy( super, :clone ); end

  protected
  def do_copy(copy, sym)
    copy.instance_eval do
      @foo = @foo.send sym
      @bar = @bar.send sym
    end

    copy
  end
end

but this does not work for #clone if the instance is frozen.

This might be better:

class Foo
  attr_accessor :foo, :bar

  def clone; copy :clone; end
  def dup; copy :dup; end

  protected
  def copy(sym)
    c = self.class.new

    copy_init c

    c.freeze? if frozen
    c.taint if tainted?

    c
  end

  def copy_init(c)
    c.instance_eval do
      @foo = @foo.sent sym
      @bar = @bar.sent sym
    end
  end
end

class Bar < Foo
  attr_accessor :name

  protected
  def copy_init(c)
    super
    c.instance_eval do
      @name = @name.send sym
    end
  end
end


> 4. When making the copy, is super the right way to make the copy, or
>    should allocate be used? (allocate doesn't work on 1.6.x, which I
>    still use heavily, though if it's the right solution, then it's the
>    right solution).

I'm inclined to follow the Java clone() pattern and use super.  Of course
you can also just do self.class.new.

> 5. Certain types cannot be dup'd (e.g. NilClass in all versions of Ruby
>    or Fixnum in 1.8 and later), so the above code will break if I write:
>
>      Foo.new(10, 42).dup
>
>    Is it possible (or is it even wise) to write a dup or clone function
>    that works with both value types and container types?

It depends on whether you expect these types as members and what you want
to be done with them.  It's difficult to anwer this generally, that's why
the automatic methods just do a shallow copy.

Generally speaking IMHO NilClass#dup should return self, same for Fixnums
and others.  But OTOH I can see why Matz did it this way: so you get to
know that some instance is not cloneable.

Kind regards

    robert