2009-09-13 17 views
8

Ruby define #clone en Objeto. Para mi sorpresa, algunas clases generan excepciones al llamarlo. He encontrado NilClass, TrueClass, FalseClass, Fixnum tener este comportamiento.¿Qué clases de Ruby admiten .clone?

1) ¿Existe una lista completa de clases (al menos core-classes), que no permiten #clone? ¿O hay una manera de detectar si una clase específica es compatible con #clone?

2) ¿Qué hay de malo en 42.clone?

+2

que realmente quieren saber cómo se prueba si una clase es cloneable mí mismo. Parece que si una clase no quiere permitirse clonar, debe hacer privado el método de clonación que hereda de Object para que pueda probar su existencia solo bajo public_methods. Parece sentido común para mí. –

Respuesta

7

No creo que haya una lista formal, al menos a menos que cuente la lectura de la fuente. La razón 2) no funciona es debido a una optimización aplicada a Fixnums. Se almacenan/transfieren internamente como sus valores reales (por lo que son verdaderos, falsos y nulos), y no como punteros. La solución ingenua es simplemente tener 42.clone devolver el mismo 42, pero entonces el invariante obj.clone.object_id != obj.object_id ya no contendría, 42.clone en realidad no se clonaría.

+0

'obj.clone.object_id! = Obj.object_id' que es verdadero y' obj. clone.object_id == obj.object_id 'no siempre son verdaderos, son diferentes. Que el primero no tenga validez no significa que el segundo no lo haga. – sawa

0

No puede clonar clases inmutables. Es decir. puede tener solo una instancia del objeto 42 (como un Fixnum), pero puede tener muchas instancias de "42" (porque la cadena es mutable). Tampoco puedes clonar símbolos ya que son algo así como cadenas inmutables.

Puede verificarlo en IRB con el método object_id. (los símbolos y los fixnums le darán el mismo object_id después de llamadas repetitivas)

+1

La mutabilidad no tiene nada que ver con eso (de hecho, puede agregar estado a un Fixnum). – Chuck

+0

Comportamiento predeterminado muy extraño para Fixnum, especialmente teniendo en cuenta que tiene el método de clonación, d.class.method_defined? (: Clone) == true –

5

Fixnum es una clase especial que recibe un trato especial por parte del idioma. Desde el momento en que se lanza el programa, hay exactamente un Fixnum para cada número que la clase puede representar, y se les da una representación especial que no requiere espacio adicional; de esta manera, las operaciones matemáticas básicas no se asignan y desasignan memoria como loca Debido a esto, no puede haber más de un 42.

Para los demás, todos tienen una cosa en común: son singletons. Solo hay una instancia de una clase singleton por definición, por lo que intentar clonarla es un error.

+1

"Debido a esto, no puede haber más de un 42.". ¿Y por qué debería haberlo? Es perfecto. –

1

Todavía no sé cómo probar clonability correctamente, pero esto es una manera muy torpe, mal para la prueba de clonablity mediante la interceptación de errores:

def clonable?(value) 
    begin 
    clone = value.clone 
    true 
    rescue 
    false 
    end 
end 

Y aquí es cómo se puede clonar incluso el unclonable. Al menos para las pocas clases con las que lo he cansado.

def super_mega_clone(value) 
    eval(value.inspect) 
end 

Aquí hay algunas pruebas de muestra:

b = :b 
puts "clonable? #{clonable? b}" 

b = proc { b == "b" } 
puts "clonable? #{clonable? b}" 

b = [:a, :b, :c] 
c = super_mega_clone(b) 

puts "c: #{c.object_id}" 
puts "b: #{b.object_id}" 
puts "b == c => #{b == c}" 
b.each_with_index do |value, index| 
    puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}" 
end 
b[0] = :z 

puts "b == c => #{b == c}" 
b.each_with_index do |value, index| 
    puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}" 
end 

b = :a 
c = super_mega_clone(b) 
puts "b: #{b.object_id} c: #{c.object_id}" 

> clonable? false 
> clonable? true 
> c: 2153757040 
> b: 2153757480 
> b == c => true 
> [0] b: 255528 c: 255528 
> [1] b: 255688 c: 255688 
> [2] b: 374568 c: 374568 
> b == c => false 
> [0] b: 1023528 c: 255528 
> [1] b: 255688 c: 255688 
> [2] b: 374568 c: 374568 
> b: 255528 c: 255528 
1

hice un git grep "can't clone" del código fuente de YARV, y consiguió

lib/singleton.rb: raise TypeError, "can't clone instance of singleton #{self.class}" 
object.c:  rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj)); 
test/test_singleton.rb: expected = "can't clone instance of singleton TestSingleton::SingletonTest" 

La primera y tercera líneas indican que no se puede clonar un producto único .

La segunda línea se refiere a rb_special_const_p(obj). Pero esto va más allá de mi conocimiento.

Cuestiones relacionadas