How to make a function accept a Slice parameter of any kind?

Asked

Viewed 93 times

0

I created this shuffle function, but I would like it to accept and return any kind of Slice

func Shuffle(ptr *[]int, seed int64) []int {

    rand.Seed(seed)
    rand.Shuffle(len(*ptr), func(i, j int) { (*ptr)[i], (*ptr)[j] = (*ptr)[j], (*ptr)[i] })


    return *ptr
}

Is there any way to do that?

2 answers

2


There is no way, you have to specify the type of input and output.


Has two solutions:

  1. Create a function for each type, which is preferable.
  2. Utilize interface{}.

If you want to go to the second case, you can do something like:

func Shuffle(ptr []interface{}, seed int64) []interface{} {
   rand.Seed(seed)

   switch ptr := ptr.(type) {
   case *[]int:
       rand.Shuffle(len(*ptr), func(i, j int) { (*ptr)[i], (*ptr)[j] = (*ptr)[j], (*ptr)[i] })
   case *[]int64:
       rand.Shuffle(len(*ptr), func(i, j int) { (*ptr)[i], (*ptr)[j] = (*ptr)[j], (*ptr)[i] })
   case *[]int32:
       rand.Shuffle(len(*ptr), func(i, j int) { (*ptr)[i], (*ptr)[j] = (*ptr)[j], (*ptr)[i] })
//...

}

    return ptr
}

Note that the result is already a interface{}, then you’ll have to use a type assertation:

valor, ok := Shuffle(....).(*[]int)
  • PS: The Slices are already "pointers by default", they are passed as a reference and are not copied, so I don’t think I have a reason to return them as a result of the function (nor to use *[]int as input. But, this is another topic.

  • Perhaps you could also use the reflect.SliceHeader with the unsafe, but I don’t know if it’s worth it.

  • Is there a difference in performance if using pointers? For me it is easier to read and understand Shuffle(&slice, seed) than Shuffle(slice, seed)

1

You can use some functions of package reflect to make this code generic, the main one is this.

func Swapper(slice interface{}) func(i, j int)

Swapper Returns a Function that swaps the Elements in the provided Slice.

Swapper panics if the provided interface is not a Slice.

It returns another function which is a changer to the Slice, that does the same thing as this line in your code:

(*ptr)[i], (*ptr)[j] = (*ptr)[j], (*ptr)[i]

Example using these features:

type AnySlice interface{}

func Shuffle(slice AnySlice, seed int64) {
    rv := reflect.ValueOf(slice)

    swapper := reflect.Swapper(rv.Interface())

    rand.Seed(seed)

    rand.Shuffle(rv.Len(), func(i, j int) {
        swapper(i, j)
    })
}

Call example:

intValues := []int{1, 2, 3, 4, 5}
strValues := []string{"a", "b", "c", "d", "e"}

seed := time.Now().UnixNano()

Shuffle(intValues, seed)
Shuffle(strValues, seed)

fmt.Printf("%#v\n%#v", intValues, strValues)

Like Slices are already "pointers", the use of & and * is unnecessary in this case.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.