Dup vs Clone in Ruby: Understanding The Differences

Did you know that you can copy an object in Ruby? Not only that, but there are two different methods to do this!

These methods are:

  • dup
  • clone

We will explore the differences in a moment, but first…

Why would you want to clone an object?

Many objects in Ruby are mutable, you can change them.

If you want to change an object but keep a copy of the original then you can clone it.

For example.

You may want an array with all the elements but the first one.

One way to do this:

a = [1,2,3,4,5]

a[1..-1]
# [2,3,4,5]

Another way would be:

b = a.clone

b.shift
# [1]

b
# [2,3,4,5]

Both examples allow you to keep the original array.

dup vs clone Ruby mindmap

Frozen Objects

Dup & clone are not an alias of each other like it happens with other Ruby methods (map/collect), there a few small differences between them.

Exploring the sameness & difference between two things is a great way to improve your understanding.

Both methods copy an object, the difference is that dup doesn’t copy the object attributes.

What object attributes?

  • frozen status
  • tainted status
  • singleton class

Here’s an example:

a = Object.new.freeze

b = a.dup
b.frozen?
# false

b = a.clone
b.frozen?
# true

Ruby 2.4 includes an option for clone to ignore the frozen status of the cloned object.

Example:

a.clone(freeze: true)
a.clone(freeze: false)

Deep vs Shallow Copying

There is more to copying an object than meets the eye.

When you make a copy, with either dup or clone, you are making a shallow copy.

This means that objects contained within other objects won’t be copied.

In other words:

If you have an array of strings, only the array will be copied, not the strings themselves.

See for yourself:

original = %w(apple orange banana)
copy     = original.clone

original.map(&:object_id)
# [23506500, 23506488, 23506476]

copy.map(&:object_id)
# [23506500, 23506488, 23506476]

The object ids are the same even after cloning the array, so we have the same strings.

You could solve that with this:

strings.clone.map(&:clone)

This results on both the array & the strings being cloned, but notice that this only goes one level deep. You can try the deep_dup method from ActiveSupport as an alternative.

Summary

You have learned about cloning objects in Ruby! Including the differences between the dup & clone methods, and shallow copy vs deep copy.

Thanks for reading!