Generic way to duplicate slices

Go 1.18

With the introduction of type parameters in Go 1.18 (currently in beta) this will be trivial to accomplish.

Based on the current proposed specs you can write a generic function like this:

func duplicateSlice[T any](src []T) []T {
    dup := make([]T, len(src))
    copy(dup, src)
    return dup
}

And use it as such:

package main

import (
    "fmt"
)

func duplicateSlice[T any](src []T) []T {
    dup := make([]T, len(src))
    copy(dup, src)
    return dup
}

func main() {
    a := []string{"foo", "bar"}
    a2 := duplicateSlice(a)

    a[0] = "baz"
    fmt.Println(a)  // [baz bar]
    fmt.Println(a2) // [foo bar]

    b := []uint64{8, 12, 30}
    b2 := duplicateSlice(b)

    b[0] = 7
    fmt.Println(b)  // [7 12 30]
    fmt.Println(b2) // [8 12 30]
}

You can run this code in this GoTip playground.


Experimental slices package

The package golang.org/x/exp/slices provides generic functions for slices. It will probably be moved into the standard lib in Go 1.19.

We can use slices.Clone to accomplish the same as with the previous duplicateSlice function:

package main

import (
    "fmt"
    "golang.org/x/exp/slices"
)

func main() {
    a := []string{"foo", "bar"}
    a2 := slices.Clone(a)

    fmt.Println(a2)  // [foo bar]
}

GoTip playground: https://gotipplay.golang.org/p/-W3_I0eYLdF


Reflection, pre-Go 1.18

For completeness, to expand on Evan's answer, here's an example of how to use reflect.Copy to accomplish the same thing before Go 1.18:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    src := []int{1,2,3}
    target := duplicateSlice(src)

    src[0] = 9
    fmt.Println(src)    // [9 2 3]
    fmt.Println(target) // [1 2 3]
}

func duplicateSlice(src interface{}) interface{} {
    t := reflect.TypeOf(src)
    if t.Kind() != reflect.Slice {
        panic("not a slice!")
    }
    v := reflect.ValueOf(src)
    
    target := reflect.MakeSlice(t, v.Cap(), v.Len())
    reflect.Copy(target, v)
    return target.Interface()
}

Keep in mind that using the reflect package will be much slower than using the approach in the currently accepted answer. Consider the code presented here as just a contrived example for reference.

Link to playground: https://play.golang.org/p/vZ1aQOFTLmU


You could write one simple statement to make a shallow copy of a slice,

b := append([]T(nil), a...)

Which is equivalent to,

b := make([]T, len(a))
copy(b, a)

For example,

package main

import "fmt"

type T int

func main() {
    a := []T{4, 2}

    b := append([]T(nil), a...)

    fmt.Println(&a[0], a, &b[0], b)
    b[0] = 9
    fmt.Println(&a[0], a, &b[0], b)
}

Output:

0x10328000 [4 2] 0x10328020 [4 2]
0x10328000 [4 2] 0x10328020 [9 2]

ADDENDUM:

Common difficulties with reflection

If people are new to Go, they shouldn't be using reflection at all.

-rob

Reflection is subtle even for experts. It exposes details whose understanding depends on knowing pretty fundamental things about how the language works and, to a lesser extent, how it is implemented. It can be bewildering even for experienced Go programmers; for newly minted Gophers there are much more important, simpler things to learn first. Those who learn reflection too early confuse themselves cloud their understanding of those fundamentals. Best to keep it at arm's length until the rest of the picture is clear.

-rob

That said,

package main

import (
    "fmt"
    "reflect"
)

func CopySlice(s interface{}) interface{} {
    t, v := reflect.TypeOf(s), reflect.ValueOf(s)
    c := reflect.MakeSlice(t, v.Len(), v.Len())
    reflect.Copy(c, v)
    return c.Interface()
}

type T int

func main() {

    {
        // append
        a := []T{4, 2}
        b := append([]T(nil), a...)
        fmt.Println(&a[0], a, &b[0], b)
        b[0] = 9
        fmt.Println(&a[0], a, &b[0], b)
    }

    {
        // make and copy
        a := []T{4, 2}
        b := make([]T, len(a))
        copy(b, a)
        fmt.Println(&a[0], a, &b[0], b)
        b[0] = 9
        fmt.Println(&a[0], a, &b[0], b)
    }

    {
        // reflection
        a := []T{4, 2}
        b := CopySlice(a).([]T)
        fmt.Println(&a[0], a, &b[0], b)
        b[0] = 9
        fmt.Println(&a[0], a, &b[0], b)
    }

}

Output:

0xc20800a200 [4 2] 0xc20800a210 [4 2]
0xc20800a200 [4 2] 0xc20800a210 [9 2]
0xc20800a290 [4 2] 0xc20800a2a0 [4 2]
0xc20800a290 [4 2] 0xc20800a2a0 [9 2]
0xc20800a310 [4 2] 0xc20800a320 [4 2]
0xc20800a310 [4 2] 0xc20800a320 [9 2]

Tags:

Generics

Go