From: Jason Merrill Date: 2006-08-29T09:59:38+09:00 Subject: Should Array.new(n, obj) be deprecated? It seems well known that the Array constructor form Array.new(n, obj) causes a common gotcha for programmers new to ruby (see * below for details). My question: is there ever any reason to use this form in favor of Array.new(n) {block} One time I can imagine someone using the first form is if they wanted to fill an array with some default value, such as 0, but generally, default or null values are singletons, or have immediate value. There is only one nil, and only one 0, and these tend to be immutable. I can't do anything to change an instance of 0 or nil that would have surprising side effects. This also means that I could just as easily write Array.new(n) {0} -or- Array.new(n) { NullObject.instance } The only way I can see preferring the first method is if you want to be able to do something to one element of an array that affects the other elements of the array. Is there ever a good reason to do this? If, for some reason I can't imagine, you wanted to get the gotcha behavior out of the second form--maybe you want an array to initially be filled with the string 'a', but you want the flexibility to easily decide later that the default really should have been 'b'--you can get this behavior from the second form without too much difficulty using a local variable. Concretely, I might use the first form to do arr = Array.new(6, 'a') arr[0].gsub!(/a/,'b') so my array went from being filled with 6 references to the same instance of 'a' to being filled with 6 references to the same instance of 'b'. I could duplicate this strange behavior using the second form by writing default = 'a' arr = Array.new(6) {default} arr[0].gsub!(/a/,'b') Now that's a little bit harder than with the first form, but that's the point. It seems like a weird and dangerous thing to do, so it should be hard, and you should have to be explicit. In light of the above, maybe Array.new(n, obj) should be deprecated, since it is both confusing, and can be replicated using the block form with only slight gymnastics. When I first started experimenting with ruby about one year ago, the first thing I did was try to port a version of Conway's Game of Life into ruby, which I had written in Java for a comp. sci. class. I wanted a grid of Cells, and tried to implement it as an array of rows, each containing an array of Cells. This gotcha got me--in a big way. "Ruby looks so simple on paper," I thought, "but if I get tripped up trying to write the game of life--what's the point." I didn't bother reading about ruby any more for a few months. Then I needed to do some heavy regex lifting, and thankfully decided to give Ruby another try. I've since fallen in love with the language, and figured out what I was doing wrong, but I might not have been so lucky. Cheers, Jason Merrill *Common Array Gotcha: If I want to make an array of 5 hashes, or of containers in general, if I only skimmed the documentation, I might try to do menus = Array.new(6, Hash.new) Of course, the array is filled with a single instance of Hash, so if I do menus[0]['dessert'] = 'cake' menus[1]['dessert'] = 'ice cream' puts menus[0]['dessert'] => 'ice cream' This is because menus[0] and menus[1] actually refer to the same object, and it's being overwritten in the second statement. What I meant to do was menus = Array.new(6) {Hash.new}, which executes the block once for each slot in the array, thus producing 6 new instances of hash.