How to convert `interface{}` that's really a slice of types whose kind is `reflect.Int32` into slice of int32?

Issue

I have the following:

type Int32A int32
type Int32B int32

and would like to implement a function that can accept any slice of types whose kind is reflect.Int32 and convert it into []int32. For example:

func ConvertTypeSliceToInt32Slice(es «es-type») []int32 {
  result := make([]int32, len(es))
  for i := 0; i < len(result); i++ {
    result[i] = es[i].(int32)
  }

  return result
}

func caller() {
  Int32as := Int32A{1, 2}
  Int32bs := Int32B{3, 5}

  int32as := ConvertTypeSliceToInt32Slice(Int32as)
  int32bs := ConvertTypeSliceToInt32Slice(Int32bs)
}

How can this be done far any arbitrary type definition whose kind is reflect.Int32? (Context: this function will be used to convert slices of proto enums; ie the full set of types is unknown and unbounded so performing a switch on each type isn’t feasible).

Also, I’m using 1.17 so I can’t use parameterized types (aka templates).

One attempt that doesn’t work (it panics at is.([]interface{})):

func ConvertTypeSliceToInt32Slice(is interface{}) []int32 {
    es := is.([]interface{})
    result := make([]int32, len(es))
    for i := 0; i < len(result); i++ {
        result[i] = es[i].(int32)
    }

    return result
}

Solution

int32 in this case is an "underlying type", and ~ in a type parameter is how you specify a constraint to an underlying type.

For example: https://go.dev/play/p/8-WAu9KlXl5

func ConvertTypeSliceToInt32Slice[T ~int32](es []T) []int32 {
    result := make([]int32, len(es))
    for i := 0; i < len(result); i++ {
        result[i] = int32(es[i])
    }

    return result
}

If you need to use reflection, you can convert the type of each element before appending to the final slice:

func ConvertTypeSliceToInt32Slice(es interface{}) []int32 {
    v := reflect.ValueOf(es)
    int32ty := reflect.TypeOf(int32(0))

    result := make([]int32, v.Len())
    for i := 0; i < v.Len(); i++ {
        result[i] = v.Index(i).Convert(int32ty).Interface().(int32)
    }
    return result
}

And if you need to ensure other convertible numeric types are not converted, you can compare the element type and panic or error as necessary:

if v.Type().Elem().Kind() != reflect.Int32 {
...

Answered By – JimB

Answer Checked By – Mildred Charles (GoLangFix Admin)

Leave a Reply

Your email address will not be published.