What is the difference between clone and dup in Ruby
When you want to protect objects from being changed inside the methods you send them, Ruby offers some methods to preserve that original objects will not get altered in method calls. These methods are: dup
and clone
which are very similar with two differences.
To demonstrate, a classic mutable hello_world
string will be used and a method alterer()
that reverses the supplied string. To avoid repetition in the code gists, a file alrerer.rb
will contain the method definition and it will be required in every case. The method will reverse the string using the method reverse!
, so the alterer.rb
file will contain the following code:
def alterer(str) str.reverse! end
Inspect the method behaviour with a normal object
The first experiment to perform is supplying the hello_world
string to the alterer
and inspect the original string object for any changes. Let's run the code below:
require "./alterer.rb" hello_world = "Hello world" alterer(hello_world) puts hello_world
By running the above gist, the hello_world
string changes inside the method alterer()
giving the following output:
dlrow olleH
The method dup
If you wanted the string to remain unchanged inside the method, you should use the dup
method, which duplicates the object leaving the original object untouched. Therefore, the previous gist should look like the following:
require "./alterer.rb" hello_world = "Hello world" alterer(hello_world.dup) puts hello_world
You can easily inspect that the output is the original string unchanged!
Hello world
The freeze
method
Another way of preventing the change of a mutable object inside a method is by freezing it, with the freeze
method. We can use the method freeze
before passing the hello_world
string to the alterer and get the following:
require "./alterer.rb" hello_world = "Hello world" hello_world.freeze alterer(hello_world) puts hello_world
In the above gist the method alterer gets a frozen object and tries to reverse it, as a result it throws a RuntimeError:
alterer.rb:2:in `reverse!': can't modify frozen String (RuntimeError)
In general, any frozen object can not get modified and there is no corresponding unfreeze method. Once you use the freeze
method in an object, the object remains frozen for the rest of its lifecycle, that’s it!
The clone
method and the difference with dup
method regarding frozen objects
Additionally, there is a similar method to dup
, the clone
method. The difference between them (regarding frozen objects) is that if you use the method dup
on a frozen object, the duplicate is not frozen, whereas when you use the clone
method on a frozen object the cloned object is also frozen!
require "./alterer.rb" hello_world = "Hello world" hello_world.freeze alterer(hello_world.dup) puts hello_world
which outputs the following:
Hello world
cloning a frozen object
require "./alterer.rb" hello_world = "Hello world" hello_world.freeze alterer(hello_world.clone) puts hello_world
which throws a RuntimeError:
alterer.rb:2:in `reverse!': can't modify frozen String (RuntimeError)
So keep in mind that duplicating a frozen object gives a NOT frozen, cloning a frozen object gives a frozen!
Preservation of singleton methods
Additionally to the difference with the frozen objects, there is one more regarding the singleton methods of the duped/cloned objects. To demonstrate this, a newly created object obj
and a singleton method m
will be used:
obj = Object.new def obj.m puts "I am a singleton method" end
With the above gist in mind, let’s inspect the difference by checking the response of a cloned and the duped object to the singleton method m
:
>> obj.clone.respond_to?(:m) => true >> obj.dup.respond_to?(:m) => false
It is clear that, the cloned object preserves all the singleton methods whereas a duped object does not. Having seen there two different behaviors (regarding frozen objects and singleton methods) we can lead to the assumption that clone
provides a deeper copy of a particular object than dup
.