Bug or not a bug? array*=int

So I'm wondering, is this a bug, or not a bug, or a type-o in the docs?

According to http://www.ruby-doc.org/core/classes/Array.html
array*int "[R]eturns a new array built by concatenating the int
copies of self."
Copies should be brand new independent copies, right?

foo=[[nil,nil,nil]]returns a new array built by concatenating the int
copies of self.
foo*=3
#Foo is now.
#[[nil,nil,nil],
# [nil,nil,nil],
# [nil,nil,nil]]

foo[1][1]=12
#Foo is now
#[[nil,12,nil],
# [nil,12,nil],
# [nil,12,nil]]

#But I would have expected it to be
#[[nil,nil,nil],
# [nil,12,nil],
# [nil,nil,nil]]

No, copies are shallow copies. Consider, for example

irb(main):002:0> class A; attr_accessor :a; end
=> nil
irb(main):003:0> a = A.new
=> #<A:0xb7c65b44>
irb(main):004:0> a.a = 10
=> 10
irb(main):005:0> foo = [a]
=> [#<A:0xb7c65b44 @a=10>]
irb(main):006:0> foo *= 3
=> [#<A:0xb7c65b44 @a=10>, #<A:0xb7c65b44 @a=10>, #<A:0xb7c65b44 @a=10>]
irb(main):007:0> a.a = 20
=> 20
irb(main):008:0> foo
=> [#<A:0xb7c65b44 @a=20>, #<A:0xb7c65b44 @a=20>, #<A:0xb7c65b44 @a=20>]

martin

···

On Wed, Oct 29, 2008 at 8:30 AM, Kyle Schmitt <kyleaschmitt@gmail.com> wrote:

So I'm wondering, is this a bug, or not a bug, or a type-o in the docs?

According to class Array - RDoc Documentation
array*int "[R]eturns a new array built by concatenating the int
copies of self."
Copies should be brand new independent copies, right?

Ahh.
And upon not being lazy and looking at the C, I see this...
ary2 = ary_new(rb_obj_class(ary), len);

Which I'm guessing is equivalent to this in ruby (which I already knew
would return the same array len times)
ary2=Array.new(ary,len)

Any chance the document maintainer could slip the word "shallow" into
the description of array*?

:slight_smile:

Dunno. But all (or almost all) copies (e.g. dup, clone, Enumerable#map, Enumerable#select) do shallow copying only. So shallow is the rule and not particularly exceptional - as it is also more efficient and deep copy is more complex (i.e. deciding when to create a new object or just copy the reference). There are quite a few discussions of this topic in the archives...

For deep copies the idiom probably most often used is

copy = Marshal.load(Marshal.dump(obj))

Kind regards

  robert

···

On 29.10.2008 17:26, Kyle Schmitt wrote:

Ahh.
And upon not being lazy and looking at the C, I see this...
ary2 = ary_new(rb_obj_class(ary), len);

Which I'm guessing is equivalent to this in ruby (which I already knew
would return the same array len times)
ary2=Array.new(ary,len)

Any chance the document maintainer could slip the word "shallow" into
the description of array*?

Kyle Schmitt wrote:

Ahh.
And upon not being lazy and looking at the C, I see this...
ary2 = ary_new(rb_obj_class(ary), len);

Which I'm guessing is equivalent to this in ruby (which I already knew
would return the same array len times)
ary2=Array.new(ary,len)

Any chance the document maintainer could slip the word "shallow" into
the description of array*?

:slight_smile:

As mentioned by others, shallow is the default (also to my knowledge in
most OO languages).
The moment you wish for deep copy is usually the moment you should
consider writing a proper class instead of deeply nesting
arrays/hashes/...

Regards
Stefan

···

--
Posted via http://www.ruby-forum.com/\.

Robert Klemme wrote:

deep copy is more complex

... and in many cases impossible (e.g. open files/sockets; procs/blocks;
any object with a singleton class)

···

--
Posted via http://www.ruby-forum.com/\.

Stefan, while I agree that in general you should write a proper class,
the case I was talking about is a completely valid and normal use of
multi-dimensional arrays in ruby. I'm guessing that duplicating a row
in an array is not a rare occurrence either. I was just saying that
maybe a mention of this should be added to the standard docs, as it's
a common pitfall. Bits are cheap :slight_smile: common pitfalls should be noted
all over the place in the docs.

--Kyle

···

On Thu, Oct 30, 2008 at 7:22 AM, Stefan Rusterholz <apeiros@gmx.net> wrote:

As mentioned by others, shallow is the default (also to my knowledge in
most OO languages).
The moment you wish for deep copy is usually the moment you should
consider writing a proper class instead of deeply nesting
arrays/hashes/...

Regards
Stefan