考虑实验包slices。该软件包是实验性的,因此我知道签名可能会发生变化;我用它来说明问题。
考虑这个包中两个函数的签名,slices.Contains
and 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
s) with E
受约束comparable
(可比较的类型)。
第一个参数Grow
相反有类型S
(just S
), with 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())
}
Even reflect.TypeOf(s).Kind()
gives 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
不是切片。只是受约束的切片类型。事实上reflect.TypeOf(s)
当 arg 是以下实例时给出不同的输出type MyUint64Slice []uint64
:
-
Contains
与精氨酸s []E
gives reflect.TypeOf(s) -> []uint64
-
Grow
与精氨酸s S
gives reflect.TypeOf(s) -> main.MyUint64Slice
然而,我并不清楚这是什么实际的两者之间的区别。
游乐场的代码:https://gotipplay.golang.org/p/zg2dGtSJwuI
Question
这两个声明在实践中是否等效?如果不是,我什么时候应该选择其中之一?