Generics 复制切片的通用方法

Generics 复制切片的通用方法,generics,go,Generics,Go,我需要复制片(以及底层数组的一部分),这样调用者就不会改变数组的原始元素。我想我可以为特定类型的数组编写一个函数: func duplicateSliceOfSomeType(sliceOfSomeType []SomeType) []SomeType { dulicate := make([]SomeType, len(sliceOfSomeType)) copy(duplicate, sliceOfSomeType) return duplicate } 但是有没有

我需要复制片(以及底层数组的一部分),这样调用者就不会改变数组的原始元素。我想我可以为特定类型的数组编写一个函数:

func duplicateSliceOfSomeType(sliceOfSomeType []SomeType) []SomeType {
    dulicate := make([]SomeType, len(sliceOfSomeType))
    copy(duplicate, sliceOfSomeType)
    return duplicate
}
但是有没有一种方法可以泛型地创建相同的方法,也许没有泛型

func duplicateSlice(slice []?) []?{
    duplicate := make([]?, len(slice))
    copy(duplicate, slice)
    return duplicate
}

你可以写一个简单的语句来制作一个切片的浅拷贝

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

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

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)
}
输出:

0x10328000 [4 2] 0x10328020 [4 2]
0x10328000 [4 2] 0x10328020 [9 2]
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]
增编:

如果人们是新手,他们根本不应该使用反射

-抢劫

即使对专家来说,反思也是微妙的。它暴露了谁的细节 理解取决于对如何理解的基本知识的了解 该语言的工作原理以及(在较小程度上)它的实现方式。信息技术 即使是有经验的围棋程序员也会感到困惑;新 铸造的地鼠有更重要、更简单的东西要学 第一。那些过早学习反思的人把自己搞糊涂了 他们对这些基本原理的理解。最好保持在手臂的位置 直到图片的其余部分清晰为止

-抢劫

也就是说

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)
    }

}
输出:

0x10328000 [4 2] 0x10328020 [4 2]
0x10328000 [4 2] 0x10328020 [9 2]
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]

您可以使用
reflect
包对任何类型进行复制,特别是
reflect.copy

在Go 1.18(2022年初发布的候选版本)中引入类型参数,这将是微不足道的

基于,您可以编写如下通用函数:

func duplicateSlice[T any](src []T) []T {
    dup := make([]T, len(src))
    copy(dup, src)
    return dup
}
并按如下方式使用:

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]
}

您可以在此中运行此代码。

为了完整性,要进一步展开,下面是一个如何使用
反射的示例。在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
}
请记住,使用
reflect
包要比当前应用程序中的方法慢得多。考虑这里给出的代码只是一个设计的例子供参考。
链接到游乐场:

所以本质上你是在为分配给
b
的新数组将
a
的元素添加到一个新的空片段中,对吗?
append
方法的性能是否与
make
/
copy
方法一样?@Kaiged:append-to-a
nil
切片转换为
make
copy
。看我修改过的答案。@Kaiged:它们是一样的。对于
a:=make([]T,256)
,BenchmarkAppend 7026 ns/op.BenchmarkMakeCopy 7054 ns/op。