What's the Difference Between Ruby'S Dup and Clone Methods

What's the difference between Ruby's dup and clone methods?

Subclasses may override these methods to provide different semantics. In Object itself, there are two key differences.

First, clone copies the singleton class, while dup does not.

o = Object.new
def o.foo
42
end

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

Second, clone preserves the frozen state, while dup does not.

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

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

The Rubinius implementation for these methods
is often my source for answers to these questions, since it is quite clear, and a fairly compliant Ruby implementation.

What's the difference between Rails dup and clone methods?

In rails 3.0, dup and clone performed essentially opposite roles as to what they do now. From ActiveRecord::Base:

Cloned objects have no id assigned and are treated as new records. Note that this is a "shallow" clone as it copies the object's attributes only, not its associations. The extent of a "deep" clone is application specific and is therefore left to the application to implement according to its need.

While it can be seen in the same file that dup simple copied the record and its attributes:

  def dup
obj = super
obj.instance_variable_set('@attributes', @attributes.dup)
obj
end

This differs from current rails 4, which defines dup and clone to more follow the note from the ruby docs, noted in a similar question not specific to rails.

In general, clone and dup may have different semantics in descendent classes. While clone is used to duplicate an object, including its internal state, dup typically uses the class of the descendent object to create the new instance.

As can be seen from the from the more current ActiveRecord source:

##
# :method: clone
# Identical to Ruby's clone method. This is a "shallow" copy. Be
# warned that your attributes are not copied. That means that modifying
# attributes of the clone will modify the original, since they will both
# point to the same attributes hash. If you need a copy of your attributes
# hash, please use the #dup method.
#
# user = User.first
# new_user = user.clone
# user.name # => "Bob"
# new_user.name = "Joe"
# user.name # => "Joe"
#
# user.object_id == new_user.object_id # => false
# user.name.object_id == new_user.name.object_id # => true
#
# user.name.object_id == user.dup.name.object_id # => false

##
# :method: dup
# Duped objects have no id assigned and are treated as new records. Note
# that this is a "shallow" copy as it copies the object's attributes
# only, not its associations. The extent of a "deep" copy is application
# specific and is therefore left to the application to implement according
# to its need.
# The dup method does not preserve the timestamps (created|updated)_(at|on).

When to use dup, and when to use clone in Ruby?

It is true that clone copies the frozen state of an object, while dup does not:

o = Object.new
o.freeze

o.clone.frozen?
#=> true

o.dup.frozen?
#=> false

clone will also copy the singleton methods of the object while dup does not:

o = Object.new
def o.foo
42
end

o.clone.respond_to?(:foo)
#=> true

o.dup.respond_to?(:foo)
#=> false

Which leads me to the assumption that clone is sometimes understood as to provide a "deeper" copy than dup. Here are some quotes about the topic:

Comment on ActiveRecord::Base#initialize_dup from Rails 3:

Duped objects have no id assigned and are treated as new records. Note
that this is a "shallow" copy as it copies the object's attributes
only, not its associations. The extent of a "deep" copy is application
specific and is therefore left to the application to implement according
to its need.

An article about deep copies in Ruby:

There is another method worth mentioning, clone. The clone method does the same thing as dup with one important distinction: it's expected that objects will override this method with one that can do deep copies.

But then again, theres deep_dup in Rails 4:

Returns a deep copy of object if it's duplicable. If it's not duplicable, returns self.

and also ActiveRecord::Core#dup and #clone in Rails 4:

clone — Identical to Ruby's clone method. This is a "shallow" copy. Be warned that your attributes are not copied. [...] If you need a copy of your attributes hash, please use the #dup method.

Which means that here, the word dup is used to refer to a deep clone again. As far as I can see, there seems to be no consensus in the community, except that you should use clone and dup in the case when you need a specific side effect of either one.

Finally, I see dup much more often in Ruby code than clone. I have never used clone so far, and I won't until I explicitly need to.

Ruby's dup and clone method for Class methods

By design, singleton methods are retained when dup is called on a Class or Module, which is what you're doing in your example. When you dup an instance, singleton methods are not retained:

user = User.new

# This is a singleton method on an Object
def user.active
'all active users'
end

cloned_user = user.clone
cloned_user.active # => 'all active users'

duped_user = user.dup
duped_user.active # => undefined method `active' for #<User:0x00007fee1f89ae30> (NoMethodError)

Notes

  • def object.method behaves the same as object.extend(module). Methods from module are not duped (with the same caveat for calling dup on Classes or Modules).
  • dup and clone internally call initialize_copy, so that's a starting point for finding how a Class overrides dup or clone.
  • Later versions of ruby added initialize_clone and initialize_dup to fine tune overrides of clone and dup.

Original variable still changes with .clone or .dup

A dup or clone in Ruby is a shallow clone, meaning that only the outer object is cloned, not its children. In your case, this means that the couples array is copied, but not each individual couple.

If you want it to be a deep clone, you need to do it manually for the stdlib arrays:

new_couples = original_couples.map { |couple| couple.clone }

If you are in a domain where copies of collections are often necessary, or you are trying to work in a more functional style, I suggest you take a look at the Hamster gem, that brings immutable data structures to ruby.

Both dup and clone return different objects, but modifying them alters the original object

dupand clone make new instances of the arrays, but not of the content, it is no deep copy.

See:

array0 = ['stuff', 'things']
array1 = array0.clone
array2 = array0.dup

puts "Array-Ids"
p array0.object_id
p array1.object_id
p array2.object_id

puts "Object ids"
array0.each_with_index{|_,i|
p array0[i].object_id
p array1[i].object_id
p array2[i].object_id
p '--------'
}

The elements inside the array share the same object_id - they are the same object. The arrays have different object ids.

When you a[0].capitalize! you modify an object, which is part in three different arrays.

See also

  • Duplicating a Ruby array of strings
  • Deep copy of arrays in Ruby
  • How to create a deep copy of an object in Ruby?

What is difference between these two methods in Ruby?

Mod::doc()

is a class method, whereas

doc()

is an instance method. Here's an example of how to use both:

class Mod   
def doc()
puts 1
end

def Mod::doc()
puts 2
end
end

a = Mod.new
a.doc #=> 1
Mod.doc #=> 2

Here's a question that compares it with

self.doc()


Related Topics



Leave a reply



Submit