Qual è la differenza tra i metodi dup e clone di Ruby?

I documenti Ruby per dup dicono:

In generale, clone e dup possono avere semantica diversa nelle classi discendenti. Mentre il clone è usato per duplicare un object, incluso il suo stato interno, dup tipicamente usa la class dell’object discendente per creare la nuova istanza.

Ma quando faccio qualche test ho scoperto che sono in realtà gli stessi:

 class Test attr_accessor :x end x = Test.new xx = 7 y = x.dup z = x.clone yx => 7 zx => 7 

Quindi quali sono le differenze tra i due metodi?

Le sottoclassi possono sovrascrivere questi metodi per fornire semantica diversa. In Object stesso, ci sono due differenze chiave.

In primo luogo, clone copia la class singleton, mentre dup non lo fa.

 o = Object.new def o.foo 42 end o.dup.foo # raises NoMethodError o.clone.foo # returns 42 

Secondo, il clone conserva lo stato congelato, mentre il dup non lo fa.

 class Foo attr_accessor :bar end o = Foo.new o.freeze o.dup.bar = 10 # succeeds o.clone.bar = 10 # raises RuntimeError 

L’ implementazione di Rubinius per questi metodi è spesso la mia fonte di risposte a queste domande, poiché è abbastanza chiara e un’implementazione di Ruby abbastanza conforms.

Quando si ha a che fare con ActiveRecord c’è anche una differenza significativa:

dup crea un nuovo object senza che sia impostato il suo id, quindi puoi salvare un nuovo object nel database premendo .save

 category2 = category.dup #=> # 

clone crea un nuovo object con lo stesso id, quindi tutte le modifiche apportate a quel nuovo object sovrascriveranno il record originale se si colpisce .save

 category2 = category.clone #=> # 

Una differenza è con oggetti congelati. Anche il clone di un object congelato viene congelato (mentre un dup di un object congelato non lo è).

 class Test attr_accessor :x end x = Test.new xx = 7 x.freeze y = x.dup z = x.clone yx = 5 => 5 zx = 5 => TypeError: can't modify frozen object 

Un’altra differenza è con i metodi singleton. La stessa storia qui, dup non copia quelli, ma il clone fa.

 def x.cool_method puts "Goodbye Space!" end y = x.dup z = x.clone y.cool_method => NoMethodError: undefined method `cool_method' z.cool_method => Goodbye Space! 

Il nuovo documento include un buon esempio:

 class Klass attr_accessor :str end module Foo def foo; 'foo'; end end s1 = Klass.new #=> # s1.extend(Foo) #=> # s1.foo #=> "foo" s2 = s1.clone #=> # s2.foo #=> "foo" s3 = s1.dup #=> # s3.foo #=> NoMethodError: undefined method `foo' for # 

Entrambi sono quasi identici, ma il clone fa una cosa in più del dup. In clone viene anche copiato lo stato congelato dell’object. In dup, sarà sempre scongelato.

  f = 'Frozen'.freeze => "Frozen" f.frozen? => true f.clone.frozen? => true f.dup.frozen? => false