I been trying to follow the Crystal specification. It shows an example of taking the address of an instance variable. So I have an analogue function, but pointing to an instance of a 2d array
def map_ptr(x : Int32, y : Int32)
pointerof(@map[x][y])
end
where the @map
array is defined somehow similar to:
@map = Array(Array(Room)).new(SIZE) { Array(Room).new(SIZE, Room.new) }
(0...SIZE).each do |x|
(0...SIZE).each do |y|
room_type = ROOMS.sample
@map[x][y] = Room.new(room_type)
end
end
When I try to use the map_ptr
function in a assignment like: current_room = map_ptr(0, 0)
, the Crystal compiler gives me an error that says only:
Error: can't take address of @map[x][y]
Why does it say @map[x][y]
and not Room
, which is the actual type it should point to? And what am I doing wrong here?
@map[x][y]
is a call to Array#[]
. You're trying to take the pointer address of its return value. That value is never assigned anywhere, so it doesn't have an address.
I assume you actually want to get the address of the element x, y
inside the @map
array.
First of all, you should be aware that this is an unsafe operation. Array
can reallocate its items, so pointers inside it can be invalidated when adding items.
If you don't intent to add items, perhaps it would be better use Slice
instead because that has more stability guarantees.
Both Array
and Slice
have a #to_unsafe
method which returns a pointer to the first item. Then it's just @map.to_unsafe[x].to_unsafe + y
to get a pointer inside the second dimension.
Example with Slice
:
class Room
end
SIZE = 5
map = Slice(Slice(Room)).new(SIZE) { Slice(Room).new(SIZE, Room.new) }
map.to_unsafe[3].to_unsafe + 2 # => Pointer(Room)@0x7f80a2b58e80