Using transmute to iterate elements of a flattened 2d array with dynamic outer dimension

If I want to iterate over the inner elements of a static 2D array using transmute is a comfortable way to go about it:

st_array: [3][2]int = { {1,2}, {3,4}, {5,6} }
for x,i in transmute([6]int) st_array do fmt.println(i, "=>", x)

with the output being unsurprising

0 => 1
1 => 2
2 => 3
3 => 4
4 => 5
5 => 6

However, iterating over a dynamic array of [2]int values in the same way

dy_array: [dynamic][2]int = {} 
append_elems(&dy_array, [2]int {1,2}, [2]int {3,4}, [2]int {5,6})
for x,i in transmute([dynamic]int) dy_array do fmt.println(i, "=>", x)

seems to only iterate over as many elements exist in the outer dimension, rather than over the entire array:

0 => 1
1 => 2
2 => 3

Sure you could just do it explicitly with a nested loop, but is there an idiomatic way to reinterpret a dynamic 2D array as a flattened array in order to iterate its inner elements that behaves in the same way as a static array?

1 Like

The simple answer for why there are only three iterations is because transmute is a bit cast conversion and preserves the length of the dynamic array.

You could use a multi-pointer to flatten it. I’m not sure if this would necessarily be considered idiomatic, but maybe someone else has a better approach.

raw := cast([^]int) raw_data(dy_array)
count := len(dy_array) * 2

for i in 0 ..< count {
	fmt.println(i, "=>", raw[i])
}
1 Like

I’m not sure if this will work for this case, but I ran into a similar problem yesterday and I did not want to cast things to a multi-pointer (which seemed like conceptual overkill, since I’m not interfacing with C code), so I used a combination of taking a slice and slice.reinterpret().

Here’s how I converted a subslice of u8s into a subslice of f32s:

data_values_u8 := input_data[DATA_OFFSET:]
data_values_f32 := slice.reinterpret([]f32, data_values_u8)

So for your case, I’m wondering if you could do this:

my_slice_int_array := dy_array[:]
my_slice_int := slice.reinterpret([]int, my_slice_int_array)

Again, I haven’t tested this, but it makes sense to me (assuming slice.reinterpret() allows for it and the underlying data of int[2]s is contiguous in memory), since reinterpret() “only converts the type and length of the slice itself.” So the underlying data pointer and data should be the same.

1 Like

This looks like the more idiomatic way to go, and I can confirm that it produces the expected results.

for x, i in slice.reinterpret([]int, dy_array[:]) {
	fmt.println(i, "=>", x)
}
0 => 1
1 => 2
2 => 3
3 => 4
4 => 5
5 => 6
1 Like

Thanks @TheNeighborlyBoy for the double check.

The nice thing too about slice.reinterpret() is that you don’t have to do any error-prone math to calculate the count.

I got nerd sniped and had to see it in action for myself, so here’s my full code and the output:

Code and output

Code:

package main

import "core:fmt"
import "core:slice"

main :: proc() {
	fmt.println("Iterating array of array with transmute:")
	st_array: [3][2]int = { {1,2}, {3,4}, {5,6} }
	for x,i in transmute([6]int) st_array do fmt.println(i, "=>", x)

	fmt.println("Iterating dynamic array of array with transmute (bad):")
	dy_array: [dynamic][2]int = {}
	append_elems(&dy_array, [2]int {1,2}, [2]int {3,4}, [2]int {5,6})
	for x,i in transmute([dynamic]int) dy_array do fmt.println(i, "=>", x)

	fmt.println("Iterating dynamic array of array with subslice and slice.reinterpret() (good):")
	my_slice_int_array := dy_array[:]
	my_slice_int := slice.reinterpret([]int, my_slice_int_array)
	for x, i in slice.reinterpret([]int, dy_array[:]) {
		fmt.println(i, "=>", x)
	}
}

The output:

Iterating array of array with transmute:
0 => 1
1 => 2
2 => 3
3 => 4
4 => 5
5 => 6
Iterating dynamic array of array with transmute (bad):
0 => 1
1 => 2
2 => 3
Iterating dynamic array of array with subslice and slice.reinterpret() (good):
0 => 1
1 => 2
2 => 3
3 => 4
4 => 5
5 => 6