ruby clone dup

¿Cuál es la diferencia entre los métodos dup y clon de Ruby?



clone (5)

Ambos son casi idénticos, pero el clon hace una cosa más que dup. En el clon, el estado congelado del objeto también se copia. En dup, siempre será descongelado.

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

Los documentos de Ruby para dup dicen:

En general, el clone y el dup pueden tener diferentes semánticas en las clases descendientes. Mientras que el clone se usa para duplicar un objeto, incluido su estado interno, dup suele usar la clase del objeto descendiente para crear la nueva instancia.

Pero cuando hago alguna prueba, me parece que en realidad son lo mismo:

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

Entonces, ¿cuáles son las diferencias entre los dos métodos?


Cuando se trata de ActiveRecord también hay una diferencia significativa:

dup crea un nuevo objeto sin que se establezca su ID, por lo que puede guardar un nuevo objeto en la base de datos .save

category2 = category.dup #=> #<Category id: nil, name: "Favorites">

clone crea un nuevo objeto con el mismo id, por lo que todos los cambios realizados en ese nuevo objeto sobrescribirán el registro original si se .save

category2 = category.clone #=> #<Category id: 1, name: "Favorites">


El documento más reciente incluye un buen ejemplo:

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


Las subclases pueden anular estos métodos para proporcionar una semántica diferente. En el Object sí, hay dos diferencias clave.

Primero, el clone copia la clase singleton, mientras que dup no.

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

En segundo lugar, el clone conserva el estado congelado, mientras que dup no lo hace.

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

La implementación de Rubinius para estos métodos suele ser mi fuente de respuestas a estas preguntas, ya que es bastante clara y una implementación de Ruby bastante compatible.


Una diferencia es con los objetos congelados. El clone de un objeto congelado también está congelado (mientras que un dup de un objeto congelado no lo está).

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

Otra diferencia es con los métodos singleton. La misma historia aquí, dup no copia eso, pero el clone hace.

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!