How to understand the #dup and #clone operate on o

2019-02-18 21:52发布

问题:

I am not sure about the meaning of "...but not the objects they reference" in both the documantion of ruby and rubinus.

In ruby-doc, there is the explanation of #clone and #dup behavior saying:

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.

The same is repeated in the implementation of Rubinius:

Copies instance variables, but does not recursively copy the objects they reference. Copies taintedness.

I tried out with the following code, but the behavior is out of my expectation.

class Klass
   attr_accessor :array
end

s1 = Klass.new
ar = [1, 2, 3]
s1.array = [ar]

s2 = s1.clone
# according to the doc,
# s2.array should be initialized with empty Array
# however the array is recursivley copied too

s2.array.equal? s1.array # true

回答1:

In Ruby, all objects are references. Take a look at the following example:

class Klass
  attr_accessor :a
end

s1 = Klass.new
a = [1,2,3]
s1.a = a
s2 = s1.clone
s1.a.object_id  #=> 7344240 
s2.a.object_id  #=> 7344240 

You can see that both of the arrays are the same object, and are both references to the array living somewhere in the heap. In a deep copy, the array itself would have been copied, and the new s2 would have its own, distinct array. The array is not copied, just referenced.

Note: Here's what it looks like if you do a deep copy:

s3 = Marshal.load(Marshal.dump(s1)) #=> #<Klass:0x00000000bf1350 @a=[1, 2, 3, 4], @bork=4> 
s3.a << 5 #=> [1, 2, 3, 4, 5] 
s1 #=> #<Klass:0x00000000e21418 @a=[1, 2, 3, 4], @bork=4> 


回答2:

The "equal?" comparison checks whether they are exactly the same object:

  • The == comparison checks whether two values are equal
  • eql? checks if two values are equal and of the same type
  • equal? checks if two things are one and the same object.

for example :

a=[1,2]
=> [1, 2] 
a == [1,2]
=> true 
a.eql? [1,2]
=> true 
a.equal? [1,2]
=> false 
a.equal? a
=> true

As you are testing using equal? it shows the copy has not made an object with the array being uninitialized, but it has made the copied object point to the same array as the original. If it recursively copied the opjects s2.array would have the same contents as s1.array but would be a different object so:

s2.array.equal? s1.array # false
s2.array.eql? s1.array # true