В чем разница между методами DUP Ruby и clone?

на Ruby docs для dup говорят:

В общем, clone и dup может иметь различную семантику в подчиненных классах. В то время как clone используется для дублирования объекта, включая его внутреннее состояние, dup обычно использует класс объекта descendent для создания нового экземпляра.

но когда я делаю какой-то тест, я обнаружил, что они на самом деле одинаковы:

class Test
   attr_accessor :x
end

x = Test.new
x.x = 7
y = x.dup
z = x.clone
y.x => 7
z.x => 7

Итак, каковы различия между этими двумя методы?

5 ответов


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

во-первых,clone копирует класс singleton, в то время как dup нет.

o = Object.new
def o.foo
  42
end

o.dup.foo   # raises NoMethodError
o.clone.foo # returns 42

во-вторых,clone сохраняет замороженное состояние, в то время как dup нет.

class Foo
  attr_accessor :bar
end
o = Foo.new
o.freeze

o.dup.bar = 10   # succeeds
o.clone.bar = 10 # raises RuntimeError

на реализация Rubinius для этих методов часто является моим источником для ответов на эти вопросы, так как это довольно ясно и довольно совместимая реализация Руби.


при работе с ActiveRecord есть существенная разница:

dup создает новый объект без его идентификатора, поэтому вы можете сохранить новый объект в базе данных, нажав .save

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

clone создает новый объект с тем же идентификатором, поэтому все изменения, внесенные в этот новый объект, перезапишут исходную запись при нажатии .save

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

одно отличие с замороженными объектами. The clone замороженного объекта также заморожен (в то время как dup замороженного объекта нет).

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

другое отличие-с Singleton-методы. Та же история и здесь,dup не копирует их, но clone делает.

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!

на новый doc включает в себя хороший пример:

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>

оба почти идентичны, но клон делает еще одну вещь, чем dup. В clone также копируется замороженное состояние объекта. В dup он всегда будет оттаивать.

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