Feature #4514

#deep_clone and #deep_dup for Objects

Added by Tom Wardrop about 3 years ago. Updated over 1 year ago.

[ruby-core:<unknown>]
Status:Assigned
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:-
Target version:next minor

Description

=begin
There's often a need to do a deep clone of an object, especially of Hash/Array trees. The typical work around to the lack of this functionality is to Marshall and then Unmarshall (e.g. Marshal::load(Marshal::dump(self)) ), which incurs more overhead than it probably should, and is not very semantic. My suggestion is to either provide #deepclone and #deepdup methods on the Object class, or to at least provide equivalent functionality for Hashes and Arrays, such as possibly a #deep_merge method for Hash. The exact implantation is not a large concern of mine; I'll let the experts determine the best method of achieving the desired outcome.
=end

History

#1 Updated by Tom Wardrop about 2 years ago

No one wants to add anything?

#2 Updated by Shyouhei Urabe about 2 years ago

Some thoughts

  • #deepmerge is a Rails method. If you only need Arrays + Hashs to be deepdup'able, chances are they also should go into Rails.
  • It is not always obvious what a "deep" copy is. For instance it is very hard to define one for a Proc instance.
  • Recursive duplication may not be that simple to implement than you imagine. For instance an Array can contain itself: r = [].tap {|r| r << r } How do you copy it deeply?

#3 Updated by Marc-Andre Lafortune about 2 years ago

Hi,

Shyouhei Urabe wrote:

  • It is not always obvious what a "deep" copy is. For instance it is very hard to define one for a Proc instance.

Agreed, but since Procs do no "contain" anything, I'd say Proc#deepdup == Proc#dup (except that it would call deepdup on any instance variables, I imagine).

  • Recursive duplication may not be that simple to implement than you imagine. For instance an Array can contain itself: r = [].tap {|r| r << r } How do you copy it deeply?

That shouldn't be too hard, we simply maintain a hash with the ids of objects being cloned as keys and with the corresponding new copies as values. I would have fun implementing it. Note that Marshal::load(Marshal::dump(r)) works for recursive arrays and so does YAML serialization.

I can attest that the question comes up from time to time on StackOverflow, so it is something quite a bit of people wonder about.

#4 Updated by Kurt Stephens about 2 years ago

Marc-Andre Lafortune wrote:

Shyouhei Urabe wrote:

  • It is not always obvious what a "deep" copy is. For instance it is very hard to define one for a Proc instance.
  • Recursive duplication may not be that simple to implement than you imagine. For instance an Array can contain itself: r = [].tap {|r| r << r } How do you copy it deeply?

That shouldn't be too hard, we simply maintain a hash with the ids of objects being cloned as keys and with the corresponding new copies as values. I would have fun implementing it. Note that Marshal::load(Marshal::dump(r)) works for recursive arrays and so does YAML serialization.

See https://github.com/kstephens/red_steak/blob/master/lib/red_steak/copier.rb

The problem is how to control how "deep" a copier should go, which objects need to have identity maintained and which objects are simply containers. The programmer needs control via some protocol with the copier.

#5 Updated by Yusuke Endoh about 2 years ago

  • Status changed from Open to Assigned
  • Assignee set to Yukihiro Matsumoto

#6 Updated by Yusuke Endoh over 1 year ago

  • Target version set to next minor

Also available in: Atom PDF