¿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 eldup
pueden tener diferentes semánticas en las clases descendientes. Mientras que elclone
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!