Consider the experimental package slices
. The package is experimental, so I understand the signatures may change; I'm using it to illustrate the issue.
考虑一下实验性的包片。这个包是试验性的,所以我知道签名可能会改变;我用它来说明这个问题。
Consider the signatures of two functions from this package, slices.Contains
and slices.Grow
:
考虑这个包中两个函数的签名,slices.Contains和slices.Grow:
func Contains[E comparable](s []E, v E) bool
Func包含[E可比](S[]E,v E)布尔
func Grow[S ~[]E, E any](s S, n int) S
Func Growth[S~[]E,E Any](S,n int)S
The first argument to Contains
has type []E
(slice of E
s) with E
constrained by comparable
(types that are comparable).
CONTAINS的第一个参数具有类型[]E(ES的切片),其中E受可比较(可比较的类型)约束。
The first argument to Grow
instead has type S
(just S
), with S
constrained by ~[]E
(types whose underlying type is a slice of E
)
相反,第一个要增长的参数具有类型S(就是S),而S受~[]E(其底层类型是E的一片的类型)约束
However it looks like there isn't any practical difference between what operations are allowed inside functions with such type params. If we declare some fake funcs with the same type parameters, we can see that both compile just fine:
然而,在具有这种类型参数的函数中允许什么操作似乎没有任何实际区别。如果我们用相同的类型参数声明一些伪函数,我们可以看到两者都编译得很好:
As expected, in both functions we can len
/cap
, append
, range
, allocate with make
, and index with [
]
.
不出所料,在这两个函数中,我们都可以使用LEN/CAP、APPED、RANGE、ALLOCATE WITH MAKE和INDEX使用[]。
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
in all cases.
即使是Reflect.TypeOf(S).Kind()也会提供反射。在所有情况下都是切片。
The functions can also be tested with different types, and all compile:
这些函数还可以用不同的类型进行测试,并且全部编译:
// 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)
}
The only actual difference in my understanding is that in slices.Grow
the argument s S
is not a slice. It's just constrained to slice types. And as a matter of fact reflect.TypeOf(s)
gives a different output when the arg is an instance of type MyUint64Slice []uint64
:
在我的理解上,唯一的实际区别是切片。S S不是切片的论点。它只是被限制为切片类型。事实上,当arg是MyUint64Slice[]uint64类型的实例时,TypeOf(S)给出不同的输出:
Contains
with arg s []E
gives reflect.TypeOf(s) -> []uint64
Grow
with arg s S
gives reflect.TypeOf(s) -> main.MyUint64Slice
However it's not immediately apparent to me what's the practical difference between the two.
然而,对我来说,这两者之间的实际区别并不是一目了然的。
Playground with the code: https://gotipplay.golang.org/p/zg2dGtSJwuI
带有代码的游乐场:https://gotipplay.golang.org/p/zg2dGtSJwuI
Question
Are these two declarations equivalent in practice? If not, when should I choose one over the other?
这两个声明在实践中是等价的吗?如果不是,我应该在什么时候选择其中一个?
更多回答
优秀答案推荐
It matters if you have to return a slice of the same (possibly named) type as the argument.
如果必须返回与参数类型相同(可能已命名)的片段,这一点很重要。
If you do not have to return a slice (just some other info e.g. a bool
to report if the value is contained), you do not need to use a type parameter that itself constraints to a slice, you may use a type parameter for the element only.
如果您不必返回切片(只需返回一些其他信息,例如,如果包含值,则报告一个布尔),则不需要使用本身约束到切片的类型参数,您可以仅对元素使用类型参数。
If you have to return a slice of the same type as the input, you must use a type parameter that itself constraints to a slice (e.g. ~[]E
).
如果必须返回与输入类型相同的片,则必须使用本身约束到片的类型参数(例如~[]E)。
To demonstrate, let's see these 2 implementations of Grow()
:
为了进行演示,我们来看一下Growth()的这两个实现:
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)]
}
If you pass a slice of a custom type having a slice as its underlying type, Grow()
can return a value of the same type. Grow2()
cannot: it can only return a value of an unnamed slice type: []E
.
如果你传递一个自定义类型的切片,切片作为其基础类型,Grow()可以返回相同类型的值。Grow2()不能:它只能返回一个未命名的切片类型的值:[]E。
And the demonstration:
演示内容如下:
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))
Output (try it on the Go Playground):
输出(在Go游乐场上尝试):
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
As you can see Grow2(y, 10)
receives a value of type main.ints
and yet it returns a value of type []int
. This is not what we would want from it.
如您所见,Grow2(y,10)接收类型为main.ints的值,但它返回类型为[]int的值。这不是我们想要的。
更多回答
我是一名优秀的程序员,十分优秀!