Instance variable still referenced after "dup"

I have a class object and I want to duplicate it with dup . One instance variable is an array, and it looks like it is referencing it. I thought dup actually created DUPLICATE.

Here is 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 not change, because dup creates a whole new variable, not a link.

Also note that if you replaced [1,2,3] with a scalar in G::initialize , dup would not refer to it.

+6
source share
3 answers

dup displays a shallow copy ; objects referenced by instance variables are not copied.

A canonical (e.g. Really Easy) hacker hack is a marshal / unmarshal that may or may not work in your actual usecase (assuming this is a simplified example). If this is not the case, or if marshalling is ineffective, the initialize_copy route is the best 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 
+6
source

The default implementation of dup and clone by default, makes a shallow copy, so you will have two objects belonging to the same array. To get the behavior you need, you must define the 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 two objects will refer to two different arrays. If there are mutable objects in arrays, you can go even deeper and dup each object in arrays:

 def initialize_copy(source) super @iv = source.iv.collect &:dup end 
+7
source

Override the dup or clone method:

  def dup Marshal::load(Marshal.dump(self)) end 
0
source

Source: https://habr.com/ru/post/904824/


All Articles