gpt4 book ai didi

go - 通用 slice 参数和限制为 slice 类型的参数有什么区别?

转载 作者:行者123 更新时间:2023-12-05 01:53:42 25 4
gpt4 key购买 nike

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

考虑这个包中两个函数的签名,slices.Containsslices.Grow:

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

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

Contains 的第一个参数的类型为 []E(E 的 slice ),E 受约束通过 comparable(可比较的类型)。

Grow 的第一个参数的类型为 S(只是 S),S~[]E(底层类型是 E 的一部分的类型)

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

正如预期的那样,在这两个函数中我们都可以len/capappendrange、分配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 。它只是 constrained slice 类型。事实上,当 arg 是 type MyUint64Slice []uint64 的实例时,reflect.TypeOf(s) 会给出不同的输出:

  • Contains with arg s []E 给出 reflect.TypeOf(s) -> []uint64
  • Grow with arg s S 给出 reflect.TypeOf(s) -> main.MyUint64Slice

但是,我并不能立即看出两者之间的实际区别是什么。

Playground 代码:https://gotipplay.golang.org/p/zg2dGtSJwuI

问题

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

最佳答案

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

如果您不必返回一个 slice (只是一些其他信息,例如一个 bool 来报告是否包含该值),您不需要使用本身约束为slice,您可以仅为元素使用类型参数。

如果必须返回与输入类型相同的 slice ,则必须使用本身限制 slice 的类型参数(例如 ~[]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)]
}

如果您传递自定义类型的一个 slice 并将 slice 作为其基础类型,Grow() 可以返回相同类型的值。 Grow2() 不能:它只能返回未命名 slice 类型的值:[]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< 的值。这不是我们想要的。

关于go - 通用 slice 参数和限制为 slice 类型的参数有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71063663/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com