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
. Theclone
method does the same thing asdup
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 asobject.extend(module)
. Methods frommodule
are notdup
ed (with the same caveat for callingdup
on Classes or Modules).dup
andclone
internally callinitialize_copy
, so that's a starting point for finding how a Class overridesdup
orclone
.- Later versions of ruby added
initialize_clone
andinitialize_dup
to fine tune overrides ofclone
anddup
.
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
dup
and 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
Bundle Install Fails With Ssl Certificate Verification Error
Tzinfo::Datasourcenotfound Error Starting Rails V4.1.0 Server on Windows
What's the Difference Between a Proc and a Lambda in Ruby
Incompatible Character Encodings: Ascii-8Bit and Utf-8
Is There a Reason That We Cannot Iterate on "Reverse Range" in Ruby
Ruby 2.4 and Rails 4 Stack Level Too Deep (Systemstackerror)
What Tools Do You Recommend to Profile Rails Apps
How to Convert a String Object into a Hash Object
Redirect_To Using Post in Rails
How to Install Sqlite3 For Ruby on Windows
Continuously Read from Stdout of External Process in Ruby
Mixing Keyword With Regular Arguments in Ruby
When to Use 'Self.Foo' Instead of 'Foo' in Ruby Methods