Why isn't there a deep copy method in Ruby?
I'm not sure why there's no deep copy method in Ruby, but I'll try to make an educated guess based on the information I could find (see links and quotes below the line).
Judging from this information, I could only infer that the reason Ruby does not have a deep copy method is because it's very rarely necessary and, in the few cases where it truly is necessary, there are other, relatively simple ways to accomplish the same task:
As you already know, using Marshal.dump
and Marshal.load
is currently the recommended way to do this. This is also the approach recommended by Programming Ruby (see excerpts below).
Alternatively, there are at least 3 available implementations found in these gems: deep_cloneable
, deep_clone
and ruby_deep_clone
; the first being the most popular.
Related Information
Here's a discussion over at comp.lang.ruby which might shed some light on this. There's another answer here with some associated discussions, but it all comes back to using Marshal
.
There weren't any mentions of deep copying in Programming Ruby, but there were a few mentions in The Ruby Programming Language. Here are a few related excerpts:
[…]
Another use for
Marshal.dump
andMarshal.load
is to create deep copies of objects:def deepcopy(o) Marshal.load(Marshal.dump(o))end
[…]
… the binary format used by
Marshal.dump
andMarshal.load
is version-dependent, and newer versions of Ruby are not guaranteed to be able to read marshalled objects written by older versions of Ruby.[…]
Note that files and I/O streams, as well as Method and Binding objects, are too dynamic to be marshalled; there would be no reliable way to restore their state.
[…]
Instead of making a defensive deep copy of the array, just call
to_enum
on it, and pass the resulting enumerator instead of the array itself. In effect, you’re creating an enumerable but immutable proxy object for your array.
Forget marshalling. The deep_dive gem will solve your problems.
Why can't you use something like this:
new_item = Item.new(old_item.attributes)new_item.save!
This would copy all the attributes from existing item to new one, without issues. If you have other objects, you can just copy them individually.
I think it's the quickest way to copy an object