Какие классы Ruby поддерживают .clone? - PullRequest
8 голосов
/ 13 сентября 2009

Ruby определяет #clone в Объект . К моему удивлению, некоторые классы вызывают исключения при вызове. Я обнаружил NilClass , TrueClass , FalseClass , Fixnum с таким поведением.

1) Существует ли полный список классов (по крайней мере, базовых классов), которые не допускают #clone? Или есть способ определить, поддерживает ли определенный класс #clone?

2) Что не так с 42.clone?

Ответы [ 6 ]

7 голосов
/ 13 сентября 2009

Я не думаю, что есть формальный список, по крайней мере, если вы не считаете чтение источника. Причина 2) не работает из-за оптимизации, примененной к Fixnums. Они хранятся / передаются внутри как их фактические значения (так же как true, false и nil), а не как указатели. Наивное решение состоит в том, чтобы просто 42.clone вернуть тот же 42, но тогда инвариант obj.clone.object_id != obj.object_id больше не будет удерживаться, 42.clone фактически не будет клонировать.

5 голосов
/ 13 сентября 2009

Fixnum - это особый класс с особым отношением к языку. С момента запуска вашей программы существует ровно один Fixnum для каждого числа, которое может представлять класс, и им дается специальное представление, которое не занимает дополнительного места - таким образом, основные математические операции не распределяются и не освобождаются память как сумасшедшая. Из-за этого не может быть больше одного 42.

Для всех остальных у них есть одна общая черта: они одиночки. По определению существует только один экземпляр одноэлементного класса, поэтому попытка его клонировать является ошибкой.

1 голос
/ 21 марта 2012

Я сделал git grep "can't clone" исходного кода YARV и получил

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"

Первая и третья строки указывают, что вы не можете клонировать синглтон.

Вторая строка относится к rb_special_const_p(obj). Но это выходит за рамки моего кен.

1 голос
/ 11 декабря 2010

Я до сих пор не знаю, как правильно проверять на клонируемость, но вот очень неуклюжий, злой способ проверить на клонируемость, используя перехват ошибок:

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

А вот как вы можете клонировать даже неклонируемого. По крайней мере, для тех немногих классов, с которыми я устал.

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

Вот несколько примеров тестирования:

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
0 голосов
/ 10 июля 2013

Rails, по-видимому, расширяет упомянутые вами классы методом "duplicable? ()".

http://api.rubyonrails.org/files/activesupport/lib/active_support/core_ext/object/duplicable_rb.html

0 голосов
/ 13 сентября 2009

Вы не можете клонировать неизменяемые классы. То есть Вы можете иметь только один экземпляр объекта 42 (как Fixnum), но можете иметь много экземпляров «42» (потому что строка является изменяемой). Вы также не можете клонировать символы, так как они являются чем-то вроде неизменяемых строк.

Вы можете проверить это в IRB с помощью метода object_id. (символы и фиксированные номера будут давать вам один и тот же object_id после повторных вызовов)

...