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

问题描述 投票:0回答:2
考虑实验包

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

的第一个参数具有类型
[]E
E
的切片),其中
E
comparable
(可比较的类型)约束。

Grow

的第一个参数具有类型
S
(只是
S
),
S
~[]E
约束(基础类型是
E
切片的类型)

然而,具有此类类型参数的函数内部允许执行的操作似乎没有任何实际差异。如果我们声明一些具有相同类型参数的伪函数,我们可以看到两者都编译得很好:

正如预期的那样,在这两个函数中,我们可以

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 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
 
不是切片。它只是“约束”到切片类型。事实上,当 arg 是 reflect.TypeOf(s) 的实例时,type MyUint64Slice []uint64
 会给出不同的输出:

Contains
    与 arg
  • s []E
     给出 
    reflect.TypeOf(s) -> []uint64
    
    
    Grow
  • 与 arg
  • s S
     给出 
    reflect.TypeOf(s) -> main.MyUint64Slice
    
    
    但是,我并不清楚两者之间的
  • 实际
区别是什么。

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

问题

这两种声明在实践中是否等效?如果不是,我什么时候应该选择其中之一?

如果
go generics slice type-parameter
2个回答
6
投票
,那么这很重要。

如果您不必返回切片(只需返回一些其他信息,例如 bool

来报告是否包含该值),则不需要使用本身限制为切片的类型参数,您可以使用类型仅元素的参数。

如果必须返回与输入类型相同的切片,则必须使用本身限制为切片的类型参数(例如 
~[]E

)。

为了演示,让我们看看 
Grow()

的这 2 个实现:

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)
接收类型为

main.ints

的值,但它返回类型为
[]int
的值。这不是我们想要的。

禁止我

0
投票
禁止我

结束我吧

用核武器攻击我

毁了我

消灭我

我是一个没有价值的人

我没有生活,没有工作,没有朋友,也没有收入

© www.soinside.com 2019 - 2024. All rights reserved.