Two dimensional array issue when created with Array.new

The two ways of creating a two dimentional array gets very different results. The first one will fail with a compiling error if commenting out the assigement statement fail[1] = [1,6,9] like
2 | fail[1] = [1,6,9] ^ Error: no overload matches 'Array(Array(Int32.class))#[]=' with types Int32, Array(Int32).

The second works as expected. Why?

fail = Array.new(3) {Array.new(3, Int32)}
#fail[1] = [1,6,9]
p fail #[[Int32, Int32, Int32], [Int32, Int32, Int32], [Int32, Int32, Int32]]

good = Array.new(3) { [] of Int32 }
good[1] = [2,5,8]
p good  # [[], [2, 5, 8], []]

The gist of it is, as you can see, they not actually doing the same thing. The equivalent code for the first example would be:

fail = Array.new(3) { Array(Int32).new 3 }
fail[1] = [1,6,9]

In that it creates an array with an initial capacity for three Int32 values. This is different than Array.new(3, Int32) which creates an array of 3 elements, with Int32.class as the value for each position. Because you didn’t provide a generic type var of Int32, it infers the type of the array as the Int32.class of which [1, 6, 9] is not compatible with, so it errors. E.g. if you did Array(Int32).new 3, Int32 the resulting error would be:

Error: no overload matches 'Array(Int32).new' with types Int32, Int32.class

Overloads are:
 - Array(T).new(size : Int, value : T)
 - Array(T).new(initial_capacity : Int)
 - Array(T).new()
 - Array(T).new(size : Int, & : (Int32 -> T))

Which makes it a bit more clear what is going on.

3 Likes