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]