3

Let's say I want to make the equivalent of the JavaScript Array.splice function in Go, for Slices. I have the following code:

func splice(slice []int, index, amount int, elements ...int) []int {
    newslice := make([]int, 0)
    for i := 0; i < index; i++ {
        newslice = append(newslice, slice[i])
    }
    for i := index + amount; i < len(slice); i++ {
        newslice = append(newslice, slice[i])
    }
    for _, el := range elements {
        newslice = append(newslice, el)
    }
    return newslice
}

This example will work, but only for arguments of type int. I want to make it generic, and I know that I should give the variadic argument elements the type interface{}, but how do I create a new slice with the type of that interface from inside the function?

In other words, how can I specify the type of the slice dynamically depending on the type of the arguments in the first line of the function, where newslice is created?

I Hate Lazy
  • 47,415
  • 13
  • 86
  • 77
Sergi Mansilla
  • 12,495
  • 10
  • 39
  • 48

2 Answers2

6

Using reflection

If you really want to do generic stuff, reflection is the ultimate answer. See the MakeSlice documentation in the reflection package for details on your problem.

You just need to retrieve the type of the incoming slice (using TypeOf(...)) and applying MakeSlice correctly.

Example of using reflection to create a slice:

y := []int{1,2,3}
t := reflect.TypeOf(y)

slice := reflect.MakeSlice(t, 0, 10)
slice = reflect.Append(slice, reflect.ValueOf(2))

fmt.Println(slice.Interface())

Run it here.

Using []interface{}

Another way to work with, is []interface{}, which can store any value but may lead to runtime panics as you omit compiler type checking completely (this is a bad thing).

Here is an example for using []interface{} as storage for arbitrary values. With this you don't need to know the type in your splice implementation, you just splice and use []interface{} for new slices.

This method has the drawback, that you can't convert some slice to []interface{} easily. You have to copy it manually, as described in posts before.

Conclusion

Regardless of which version you use, you will never get back type safety without knowing the type and converting it back manually. There's no such thing in Go which will do that for you. That means, that you'll have something like this in your code to regain type safety:

x := []int{1,2,3,4}
y := splice(x, ...)
yn := []int(y)
Community
  • 1
  • 1
nemo
  • 55,207
  • 13
  • 135
  • 135
5

Instead of emulating JavaScript in Go (why ???) I would like to suggest to compose simmilar required operations from the building blocks of SliceTricks.

They are:

  • Completely type agnostic (think "generics" for free).
  • Quite probably pretty faster compared to packing/unpacking whatsoever in/from a []interface{}.
zzzz
  • 87,403
  • 16
  • 175
  • 139
  • That link is immensely helpful, thanks! I am porting some JavaScript code, and first making it work instead of making it idiomatic right away. That's why I was looking into emulating splice. – Sergi Mansilla Dec 07 '12 at 07:43