为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕虎7371278 2022-11-08 15:11:46
考虑实验包slices。这个包是实验性的,所以我知道签名可能会改变;我用它来说明问题。考虑这个包中两个函数的签名,slices.Contains并且slices.Grow:func Contains[E comparable](s []E, v E) boolfunc Grow[S ~[]E, E any](s S, n int) S的第一个参数Contains具有类型[]E(Es 的切片)和E受约束的comparable类型(可比较的类型)。Grow相反,第一个参数的类型为S(just S),受(基础类型为 的切片的类型)S约束~[]EE但是,在具有此类参数的函数内部允许的操作之间似乎没有任何实际区别。如果我们声明一些具有相同类型参数的假函数,我们可以看到两者都编译得很好:正如预期的那样,在这两个函数中,我们可以len/ cap、append、range、 分配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 finefunc 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) -> []uint64Grow带 args S给出reflect.TypeOf(s) -> main.MyUint64Slice但是,对我来说,两者之间的实际区别并不是很明显。带有代码的游乐场:https ://gotipplay.golang.org/p/zg2dGtSJwuI问题这两个声明在实践中是否等效?如果没有,我应该什么时候选择一个而不是另一个?
查看完整描述

1 回答

?
犯罪嫌疑人X

TA贡献2080条经验 获得超4个赞

如果您必须返回与参数相同(可能命名)类型的切片,这很重要。


如果您不必返回切片(仅返回一些其他信息,例如 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 []int

y := 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=12

x3 []int len=1 cap=12

y2 main.ints len=1 cap=12

y3 []int len=1 cap=12

如您所见Grow2(y, 10),接收一个 type 的值main.ints,但它返回一个 type 的值[]int。这不是我们想要的。


查看完整回答
反对 回复 2022-11-08
  • 1 回答
  • 0 关注
  • 73 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信