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
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.
ยทยทยท
from :0