通用切片参数和限制为切片类型的参数有什么区别?

考虑实验包slices。这个包是实验性的,所以我知道签名可能会改变;我用它来说明问题。

考虑这个包中两个函数的签名,slices.Contains并且slices.Grow

  • func Contains[E comparable](s []E, v E) bool

  • func Grow[S ~[]E, E any](s S, n int) S

的第一个参数Contains具有类型[]EEs 的切片)和E受约束的comparable类型(可比较的类型)。

Grow相反,第一个参数的类型为S(just S),受(基础类型为 的切片的类型)S约束~[]EE

但是,在具有此类参数的函数内部允许的操作之间似乎没有任何实际区别。如果我们声明一些具有相同类型参数的假函数,我们可以看到两者都编译得很好:

正如预期的那样,在这两个函数中,我们可以lencapappendrange、 分配make和 索引[ ]

func fakeContains[E comparable](s []E, v E) {

    fmt.Println(len(s), cap(s))


    var e E

    fmt.Println(append(s, e))

    fmt.Println(make([]E, 4))


    for _, x := range s {

        fmt.Println(x)

    }

    fmt.Println(s[0])

    

    fmt.Println(reflect.TypeOf(s).Kind())

}


func fakeGrow[S ~[]E, E any](s S, n int) {

    fmt.Println(len(s), cap(s))


    var e E

    fmt.Println(append(s, e))

    fmt.Println(make(S, 4))


    for _, x := range s {

        fmt.Println(x)

    }

        fmt.Println(s[0])

    

    fmt.Println(reflect.TypeOf(s).Kind())

}

甚至在所有情况下都reflect.TypeOf(s).Kind()给予。reflect.Slice


这些函数也可以用不同的类型进行测试,并且都可以编译:


// compiles just fine

func main() {

    type MyUint64 uint64

    type MyUint64Slice []uint64


    foo := []uint64{0, 1, 2}

    fakeContains(foo, 0)

    fakeGrow(foo, 5)


    bar := []MyUint64{3, 4, 5}

    fakeContains(bar, 0)

    fakeGrow(bar, 5)


    baz := MyUint64Slice{6, 7, 8}

    fakeContains(baz, 0)

    fakeGrow(baz, 5)

}

在我的理解中,唯一的实际区别是在slices.Grow参数s S 中不是 slice。它只限于切片类型。事实上reflect.TypeOf(s),当 arg 是 的实例时会给出不同的输出type MyUint64Slice []uint64

  • Contains带 args []E给出reflect.TypeOf(s) -> []uint64

  • Grow带 args S给出reflect.TypeOf(s) -> main.MyUint64Slice

但是,对我来说,两者之间的实际区别并不是很明显。

带有代码的游乐场:https ://gotipplay.golang.org/p/zg2dGtSJwuI

问题

这两个声明在实践中是否等效?如果没有,我应该什么时候选择一个而不是另一个?


慕虎7371278
浏览 74回答 1
1回答

犯罪嫌疑人X

如果您必须返回与参数相同(可能命名)类型的切片,这很重要。如果您不必返回切片(仅返回一些其他信息,例如 abool以报告是否包含该值),则无需使用本身约束切片的类型参数,您可以为元素使用类型参数只要。如果必须返回与输入相同类型的切片,则必须使用本身约束到切片的类型参数(例如~[]E)。为了演示,让我们看看这两个实现Grow():func Grow[S ~[]E, E any](s S, n int) S {    return append(s, make(S, n)...)[:len(s)]}func Grow2[E any](s []E, n int) []E {    return append(s, make([]E, n)...)[:len(s)]}如果您传递具有切片作为其基础类型的自定义类型的切片,Grow()则可以返回相同类型的值。Grow2()不能:它只能返回一个未命名的切片类型的值:[]E。和演示:x := []int{1}x2 := Grow(x, 10)fmt.Printf("x2 %T len=%d cap=%d\n", x2, len(x2), cap(x2))x3 := Grow2(x, 10)fmt.Printf("x3 %T len=%d cap=%d\n", x3, len(x3), cap(x3))type ints []inty := ints{1}y2 := Grow(y, 10)fmt.Printf("y2 %T len=%d cap=%d\n", y2, len(y2), cap(y2))y3 := Grow2(y, 10)fmt.Printf("y3 %T len=%d cap=%d\n", y3, len(y3), cap(y3))输出(在Go Playground上试试):x2 []int len=1 cap=12x3 []int len=1 cap=12y2 main.ints len=1 cap=12y3 []int len=1 cap=12如您所见Grow2(y, 10),接收一个 type 的值main.ints,但它返回一个 type 的值[]int。这不是我们想要的。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go