According to Ruby-doc, both #clone and #dup can be used to create a shallow copy of an object, which only traverse one layer of complexity, meaning that the instance variables of obj are copied, but not the objects they reference. They would all share the same attributes; modifying one would result a change on another.
It uses #dup to allow you to duplicate a record without its "internal" state (id and timestamps), and leaves #clone up to Ruby to implement. Having this extra method also asks for a specific initializer when using the #clone method.
Shallow copyIf a variable of the copied object is a reference to another object, then just the reference address of the object is copied.
Ruby does provide two methods for making copies of objects, including one that can be made to do deep copies. The Object#dup method will make a shallow copy of an object. To achieve this, the dup method will call the initialize_copy method of that class. What this does exactly is dependent on the class.
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.
When dealing with ActiveRecord there's a significant difference too:
dup
creates a new object without its id being set, so you can save a new object to the database by hitting .save
category2 = category.dup
#=> #<Category id: nil, name: "Favorites">
clone
creates a new object with the same id, so all the changes made to that new object will overwrite the original record if hitting .save
category2 = category.clone
#=> #<Category id: 1, name: "Favorites">
One difference is with frozen objects. The clone
of a frozen object is also frozen (whereas a dup
of a frozen object isn't).
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
Another difference is with singleton methods. Same story here, dup
doesn't copy those, but clone
does.
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!
The newer doc includes a good example:
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>
Both are nearly identical but clone does one more thing than dup. In clone, the frozen state of the object is also copied. In dup, it’ll always be thawed.
f = 'Frozen'.freeze
=> "Frozen"
f.frozen?
=> true
f.clone.frozen?
=> true
f.dup.frozen?
=> false
You can use clone to do prototype-based programming in Ruby. Ruby's Object class defines both the clone method and dup method. Both clone and dup produce a shallow copy of the object it is copying; that is, the instance variables of the object are copied but not the objects they reference. I will demonstrate an example:
class Apple
attr_accessor :color
def initialize
@color = 'red'
end
end
apple = Apple.new
apple.color
=> "red"
orange = apple.clone
orange.color
=> "red"
orange.color << ' orange'
=> "red orange"
apple.color
=> "red orange"
Notice in the above example, the orange clone copies the state (that is, the instance variables) of the apple object, but where the apple object references other objects (such as the String object color), those references are not copied. Instead, apple and orange both reference the same object! In our example, the reference is the string object 'red'. When orange uses the append method, <<, to modify the existing String object, it changes the string object to 'red orange'. This in effect changes apple.color too, since they are both pointing to the same String object.
As a side note, the assignment operator, =, will assign a new object and thus destroy a reference. Here is a demonstration:
class Apple
attr_accessor :color
def initialize
@color = 'red'
end
end
apple = Apple.new
apple.color
=> "red"
orange = apple.clone
orange.color
=> "red"
orange.color = 'orange'
orange.color
=> 'orange'
apple.color
=> 'red'
In the above example, when we assigned a fresh new object to the color instance method of the orange clone, it no longer references the same object as apple. Hence, we can now modify the color method of orange without affecting the color method of apple, but if we clone another object from apple, that new object will reference the same objects in copied instance variables as apple.
dup will also produce a shallow copy of the object it is copying, and if you were to do the same demonstration shown above to dup, you will see it works exactly the same way. But there are two major differences between clone and dup. First, as others mentioned, clone copies the frozen state and dup does not. What does this mean? The term 'frozen' in Ruby is an esoteric term for immutable, which itself is a nomenclature in computer science, meaning that something cannot be changed. Thus, a frozen object in Ruby cannot be modified in any way; it is, in effect, immutable. If you attempt to modify a frozen object, Ruby will raise a RuntimeError exception. Since clone copies the frozen state, if you attempt to modify a cloned object, it will raise a RuntimeError exception. Conversely, since dup does not copy the frozen state, no such exception will occur, as we'll demonstrate:
class Apple
attr_accessor :color
def initialize
@color = 'red'
end
end
apple = Apple.new
apple.frozen?
=> false
apple.freeze
apple.frozen?
=> true
apple.color = 'crimson'
RuntimeError: can't modify frozen Apple
apple.color << ' crimson'
=> "red crimson" # we cannot modify the state of the object, but we can certainly modify objects it is referencing!
orange = apple.dup
orange.frozen?
=> false
orange2 = apple.clone
orange2.frozen?
=> true
orange.color = 'orange'
=> "orange" # we can modify the orange object since we used dup, which did not copy the frozen state
orange2.color = 'orange'
RuntimeError: can't modify frozen Apple # orange2 raises an exception since the frozen state was copied via clone
Second, and, more interestingly, clone copies the singleton class (and hence its methods)! This is very useful if you desire to undertake prototype-based programming in Ruby. First, let's show that indeed the singleton methods are copied with clone, and then we can apply it in an example of prototype-based programming in Ruby.
class Fruit
attr_accessor :origin
def initialize
@origin = :plant
end
end
fruit = Fruit.new
=> #<Fruit:0x007fc9e2a49260 @origin=:plant>
def fruit.seeded?
true
end
2.4.1 :013 > fruit.singleton_methods
=> [:seeded?]
apple = fruit.clone
=> #<Fruit:0x007fc9e2a19a10 @origin=:plant>
apple.seeded?
=> true
As you can see, the singleton class of the fruit object instance is copied to the clone. And hence the cloned object has access to the singleton method :seeded?. But this is not the case with dup:
apple = fruit.dup
=> #<Fruit:0x007fdafe0c6558 @origin=:plant>
apple.seeded?
=> NoMethodError: undefined method `seeded?'
Now in prototype-based programming, you do not have classes which extend other classes and then create instances of classes whose methods derive from a parent class that serves as a blueprint. Instead, you have a base object and then you create a new object from the object with its methods and state copied over (of course, since we are doing shallow copies via clone, any objects the instance variables reference will be shared just as in JavaScript prototypes). You can then fill in or change the object's state by filling in the details of the cloned methods. In the below example, we have a base fruit object. All fruit have seeds, so we create a method number_of_seeds. But apples have one seed, and so we create a clone and fill in the details. Now when we clone apple, we not only cloned the methods but we cloned the state! Remember clone does a shallow copy of the state (instance variables). And because of that, when we clone apple to get a red_apple, red_apple will automatically have 1 seed! You can think of red_apple as an object that inherits from Apple, which in turn inherits from Fruit. Hence, that is why I capitalized Fruit and Apple. We did away with the distinction between classes and objects courtesy of clone.
Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
@number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
@number_of_seeds
end
Apple = Fruit.clone
=> #<Object:0x007fb1d78165d8>
Apple.number_of_seeds = 1
Apple.number_of_seeds
=> 1
red_apple = Apple.clone
=> #<Object:0x007fb1d892ac20 @number_of_seeds=1>
red_apple.number_of_seeds
=> 1
Of course, we can have a constructor method in protoype-based programming:
Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
@number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
@number_of_seeds
end
def Fruit.init(number_of_seeds)
fruit_clone = clone
fruit_clone.number_of_seeds = number_of_seeds
fruit_clone
end
Apple = Fruit.init(1)
=> #<Object:0x007fcd2a137f78 @number_of_seeds=1>
red_apple = Apple.clone
=> #<Object:0x007fcd2a1271c8 @number_of_seeds=1>
red_apple.number_of_seeds
=> 1
Ultimately, using clone, you can get something similar to the JavaScript prototype behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With