From: Brian Candler Date: 2008-08-27T18:12:07+09:00 Subject: Re: how to avoid passing by reference and how to copy objects > In Ruby its just by reference That's one of the joyful aspects of Ruby, especially coming from Perl (am I passing a scalar? A reference to an array or hash? A filehandle? A typeglob? Argh!) Similarly from C++ (am I passing a value? A pointer? A reference?!) > I want to pass the array to a function. This function will take the > array, make a new copy so as to protect the original from any changes. > Changes will be made to the copy and then the copy is returned. > > How do i do that? > > Ive looked dup but i dont think it works for objects Everything's an object in Ruby. The only things you can't dup are certain immutable primitive objects (e.g. nil.dup, 3.dup, :foo.dup), and they don't need to be dup'd anyway. If you dup an Array you will get a new array, but all the slots in that array will point to the same objects as the slots in the old array. This causes aliasing: irb(main):001:0> a = ["foo","bar"] => ["foo", "bar"] irb(main):002:0> b = a.dup => ["foo", "bar"] irb(main):003:0> b[0] = "baz" => "baz" irb(main):004:0> b => ["baz", "bar"] irb(main):005:0> a => ["foo", "bar"] irb(main):006:0> b[1] << "xxx" => "barxxx" irb(main):007:0> b => ["baz", "barxxx"] irb(main):008:0> a => ["foo", "barxxx"] irb(main):009:0> So maybe what you're thinking of is a "deep copy", which recursively duplicates the object and all the other objects refered to therein (remembering that they may refer to each other in a circular fashion). The easiest way to do this is to Marshal the graph of objects into a string, and create a new graph of objects from that string. irb(main):009:0> a = ["foo", "bar"] => ["foo", "bar"] irb(main):010:0> b = Marshal.load(Marshal.dump(a)) => ["foo", "bar"] irb(main):011:0> b[1] << "xxx" => "barxxx" irb(main):012:0> b => ["foo", "barxxx"] irb(main):013:0> a => ["foo", "bar"] irb(main):014:0> In practice, few Ruby programs actually do this. It's much more common for Ruby code to modify complex data structures in-place (*). If you come from a functional programming background, you may find this distasteful :-) OTOH, if you are enforcing a regime where individual objects cannot be mutated, then you don't need a deep copy in the first place: irb(main):014:0> a = ["foo", "bar"] => ["foo", "bar"] irb(main):015:0> a.each { |e| e.freeze } => ["foo", "bar"] irb(main):016:0> a.freeze => ["foo", "bar"] irb(main):017:0> b = a.dup => ["foo", "bar"] irb(main):018:0> b[0] = "baz" => "baz" irb(main):019:0> b[1] << "xxx" TypeError: can't modify frozen string from (irb):19:in `<<' from (irb):19 from :0 irb(main):020:0> b[1] = b[1] + "xxx" => "barxxx" irb(main):021:0> b => ["baz", "barxxx"] irb(main):022:0> a => ["foo", "bar"] irb(main):023:0> Usually you wouldn't go so far as to freeze all objects, as that's just a pain, but you can have it implicit in your contract with the caller that you promise not to modify any objects which are passed to you. In this case, returning a new Array which points to (some of) the same objects as the old Array is reasonable behaviour. Usually what happens is that the caller forgets about the old Array, leaving it to be garbage collected. Any objects which are referenced only by the old Array are also garbage-collected at that time, but those referenced by the new Array live on. HTH, Brian. (*) Especially since some objects cannot be Marshalled, e.g. IO objects, Proc objects.