#dup vs #clone in Ruby and Rails
I was recently fixing a failing test and discovered that Ruby and Rails implement
#dup in confusing and occasionally opposite ways.
#clone is a less complete copy of an object than
Rails versions have flip-flopped on how to implement
#dup, and there is ambiguity in how Rails defines “shallow”. In Rails 4.0,
#clone is a shallow copy of an ActiveRecord object. “Shallow” in this context means that the
clone shares attributes with the
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.
#dup is also described as a shallow copy. “Shallow” in this context means that while the dup does not share attributes with the original, it does share associations.
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.
dup in Rails:
1 2 3 4 5 6 7 8 9
Note that the
clone_copy is an exact copy of the original (same
user.id) and the
dup_copy is a new record (
user.id = nil). Any changes made to the
clone_copy will be changed in the
original, but any changes to the
dup_copy attributes will remain isolated.
#clone is a more complete copy of an object than
With simple classes,
dup() behave identically:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
dup() function the same way!
clone() from the Ruby docs:
Produces a shallow copy of obj — the instance variables of obj are copied, but not the objects they reference. Copies the frozen and tainted state of obj. See also the discussion under Object#dup.
dup() from the Ruby docs looks suspiciously like the docs for
Produces a shallow copy of obj — the instance variables of obj are copied, but not the objects they reference. dup copies the tainted state of obj. This method may have class-specific behavior. If so, that behavior will be documented under the #initialize_copy method of the class.
This deserves further clarification:
In general, clone and dup may have different semantics in descendant classes. While clone is used to duplicate an object, including its internal state, dup typically uses the class of the descendant object to create the new instance. When using dup any modules that the object has been extended with will not be copied.
#dup will act like
#clone, but without the original’s singleton class (ergo a “shallower” copy).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
There are subtle differences between
#dup in Ruby, and less subtle differences in Rails (depending on your version). Take care that the object you want is the object you get.