I have an object of a class, and I want to duplicate it with dup
. One of the instance variables is an array, and it seems to be referencing it. I thought dup actually created a DUPLICATE.
Here's my IRB session:
irb(main):094:0> class G
irb(main):095:1> attr_accessor :iv
irb(main):096:1> def initialize
irb(main):097:2> @iv = [1,2,3]
irb(main):098:2> end
irb(main):099:1> end
=> nil
irb(main):100:0> a=G.new
=> #<G:0x27331f8 @iv=[1, 2, 3]>
irb(main):101:0> b=a.dup
=> #<G:0x20e4730 @iv=[1, 2, 3]>
irb(main):103:0> b.iv<<4
=> [1, 2, 3, 4]
irb(main):104:0> a
=> #<G:0x27331f8 @iv=[1, 2, 3, 4]
I would expect a
to be unchanged, because dup
creates a whole new variable, not reference.
Also note that if you were to replace [1,2,3]
with a scalar in G::initialize
, dup
will not reference it.
The default implementation of dup
and clone
just make a shallow copy, so you will have two objects referring to the same array. To get the behavior that you want, you should define an initialize_copy
function (which is called by dup
and clone
):
class G
attr_accessor :iv
def initialize_copy(source)
super
@iv = source.iv.dup
end
end
Then the two objects will refer to two different arrays. If the arrays have mutable objects in them, you might want to go even deeper and dup
each object in the arrays:
def initialize_copy(source)
super
@iv = source.iv.collect &:dup
end
dup
crates a shallow copy; the objects referred to by instance variables are not copied.
The canonical (e.g., Really Easy) deep copy hack is to marshal/unmarshal, which may or may not work in your actual usecase (assuming this is a simplified example). If it doesn't, or if marshalling is to inefficient, the initialize_copy
route is a better option.
pry(main)> a = G.new
=> #<G:0x9285628 @iv=[1, 2, 3]>
pry(main)> b = a.dup
=> #<G:0x92510a8 @iv=[1, 2, 3]>
pry(main)> a.iv.__id__
=> 76819210
pry(main)> b.iv.__id__
=> 76819210
pry(main)> b = Marshal::load(Marshal.dump(a))
=> #<G:0x9153c3c @iv=[1, 2, 3]>
pry(main)> a.__id__
=> 76819220
pry(main)> b.__id__
=> 76193310
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With