Alasdair
Alasdair

Reputation: 14161

Golang: how to use interface{} type to insert a value into the middle of a slice?

I'm having difficulty getting my head around the usage of interface{} types with Go.

In this instance I have a function which inserts a value into the middle of a slice somewhere. It looks like this:

type mystruct {
    a, b, c int
}

func insert(ar []mystruct, val mystruct, i int) []mystruct {
    l := len(ar)
    if l == cap(ar) {
        tmp := make([]mystruct, l + 1, (l * 2) + 1)
        copy(tmp, ar[0:i])
        copy(tmp[i+1:], ar[i:])
        ar = tmp
    } else {
        ar = ar[0:l+1]
        copy(ar[i+1:], ar[i:])
    }
    ar[i] = val

    return ar
}

I'd like for that function to be able to accept interface{} for both ar and val so that I could pass it a slice and value of any type and it would perform the insert without complaint.

From what I have read so far I believe this should be done with the reflect package. I have read the Rules of Reflection and various tutorials on reflection and interface{}, but I'm not sure how to approach this. I'd like to minimize overhead as much as possible, I understand reflection is going to slow the code down a bit but what is the most efficient way of doing this?

Thanks!

Upvotes: 3

Views: 2630

Answers (1)

thwd
thwd

Reputation: 24898

The whole "grow if at capactiy" code is unnecessary; use the built-in append – it does exactly that for you.

Change the signature to:

func insert(ar interface{}, val interface{}, i int) interface{}

Then check that ar is a slice and that the element type of ar is the same type as val:

at, vt := reflect.TypeOf(ar), reflect.Typeof(val)

if at.Kind() != reflect.Slice {
    panic(fmt.Sprintf("expected slice, got %T", at))
}

if at.Elem() != vt {
    panic("first argument must be a slice of the second argument's type")
}

After type validation, get a hold of some reflect.Values:

av, vv := reflect.ValueOf(ar), reflect.ValueOf(val)

And get moving stuff around using e.g:

  • (reflect.Value).Index(int) to access element of the slice.
  • reflect.Copy to copy to and from slices à la built-in copy
  • reflect.Append to append to a slice Value
  • (reflect.Value).Set(reflect.Value) to set the value of a settable element (something you got out of a slice, for instance).
  • reflect.Append or reflect.AppendSlice to append to a slice à la built-in append.

Once you have your final reflect.Value representing the result slice; return it as an interface{} like this:

return result.Interface()

Upvotes: 1

Related Questions