Quali classi di Ruby supportano .clone?
Domanda
Ruby definisce #clone
in Oggetto .
Con mia sorpresa, alcune classi generano eccezioni quando la chiamano.
Ho trovato NilClass , TrueClass , FalseClass , Fixnum con questo comportamento.
1) Esiste un elenco completo di classi (almeno classi principali) che non consentono #clone
?
Oppure c'è un modo per rilevare se una classe specifica supporta #clone
?
2) Cosa c'è di sbagliato in 42.clone
?
Soluzione
Non penso che ci sia un elenco formale, almeno a meno che tu non conti la lettura della fonte. Il motivo 2) non funziona a causa di un'ottimizzazione applicata a Fixnums. Sono memorizzati / passati internamente come valori effettivi (quindi sono true, false e nil) e non come puntatori. La soluzione ingenua è semplicemente che 42.clone
restituisca lo stesso 42
, ma poi l'invariante obj.clone.object_id! = Obj.object_id
non reggerebbe più, 42.clone
in realtà non clonerebbe.
Altri suggerimenti
Fixnum è una classe speciale data un trattamento speciale dalla lingua. Dal momento in cui il tuo programma viene avviato, esiste esattamente un Fixnum per ogni numero che la classe può rappresentare e viene data una rappresentazione speciale che non occupa spazio extra & # 8212; in questo modo, le operazioni matematiche di base non stanno allocando e deallocando la memoria come un matto. Per questo motivo, non ci può essere più di un 42.
Per gli altri, hanno tutti una cosa in comune: sono singoli. C'è solo un'istanza di una classe singleton per definizione, quindi cercare di clonarla è un errore.
Non so ancora come testare la clonabilità in modo corretto, ma ecco un modo molto volgare e malvagio per testare la clonabilità usando il trapping degli errori:
def clonable?(value)
begin
clone = value.clone
true
rescue
false
end
end
Ed ecco come puoi clonare anche l'inconveniente. Almeno per le pochissime lezioni con cui l'ho stancato.
def super_mega_clone(value)
eval(value.inspect)
end
Ecco alcuni test di esempio:
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
Ho fatto un git grep " impossibile clonare "
del codice sorgente di YARV, e ho ottenuto
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 prima e la terza riga indicano che non è possibile clonare un singleton.
La seconda riga si riferisce a rb_special_const_p (obj)
. Ma questo va oltre la mia comprensione.
Non puoi clonare classi immutabili. Cioè puoi avere solo un'istanza dell'oggetto 42 (come Fixnum), ma puoi avere molte istanze di " 42 " (perché la stringa è mutabile). Non puoi clonare simboli anche perché sono qualcosa di simile a stringhe immutabili.
Puoi verificarlo in IRB con il metodo object_id. (simboli e fixnum ti daranno lo stesso object_id dopo chiamate ripetitive)
Rails sembra estendere le classi menzionate con un " duplicabile? () " metodo.