On Tue, Jun 15, 2004 at 12:08:37AM +0900, Robert Klemme wrote: > You need to return 'copy' here. Good catch, thanks. > > 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 I like this, since it works on both 1.6 and 1.8. > but this does not work for #clone if the instance is frozen. Right, because the copy has already been frozen when we start to modify it. I wonder if there's a good way around that (without using initialize_copy, since that doesn't work on 1.6). Maybe I should re-implement clone/dup in ruby (as you've done below) for Object so that it has the same behavior as 1.8. > This might be better: > It's much better, though it seems a bit heavyweight. > 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? I think this should read: if sym == :clone then c.freeze if frozen? c.taint if tainted? end > > 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. The problem with self.class.new is that the initialize method may have some semantics that are undesirable when copying. > > 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. That's exactly what I was thinking, though I was hoping for a better answer. Paul