The Crystal Programming Language Forum

Why are types acting different with ivars vs local variables?

https://play.crystal-lang.org/#/r/8dmj

class Zone
  property update_buffer = Array(Hash(Int64, Array(Array(Int32 | String)))).new
end

zone1 = Zone.new
#zone1.update_buffer += [[0, [0]]]
#pp zone1.update_buffer

total_game_buffer = Array(Hash(Int64, Array(Array(Int32 | String)))).new
total_game_buffer += [[0, [0]]]

pp total_game_buffer

I just extracted this out of my game loop because I was like wtf is going on :D

Questions:

  • Why does total_game_buffer work, when [[0, [0]]] does not match an Array of Hashes?
  • How come the type declaration of Array(Hash(Int64, Array(Array(Int32 | String)))).new gives an error if it’s inside a class and assigned to an ivar, but not with a local variable?

a += b expands to a = a + b. The type of a after this expression is determined by the result of the right hand side expression: typeof(a) = typeof(a + b). The method Array#+ concatenates two arrays and returns the result. The generic type argument of the returned array is the union of the two operands’ generic type arguments: Array(T)#+(Array(U)) returns Array(A | U).

That should explain the first question.
The second answer: Local variables behave the same as ivars if you declare a type restriction. Usually you wouldn’t do that (like in the example) and thus the compiler can change the type of the local variable according to the result type assigned to it.
You can insert a few p typeof(total_game_buffer) to see that.

1 Like

Thanks for the explanation.

and thus the compiler can change the type of the local variable according to the result type assigned to it.

Aww. Interesting

I’m going to refactor some stuff with this new info ;)