如何在 Go 中创建泛型方法? (方法必须没有类型参数)
How to create generic method in Go? (method must have no type parameters)
Golang 1.18beta 支持泛型,我想在泛型切片上添加扩展方法。例如地图函数定义如下:
func Map[E, V any](slice *[]E, iteratee func(E) V) *[]V {
result := []V{}
for _, item := range *slice {
result = append(result, iteratee(item))
}
return &result
}
然后想把这个方法做成slice的扩展方法,类似这样,但是编译不成功:
func (slice *[]E) Map[E, V any](iteratee func(E) V) *[]V {
result := []V{}
for _, item := range *slice {
result = append(result, iteratee(item))
}
return &result
}
go build
与 Go 1.18 给出错误:
main.go: method must have no type parameters
实现第二个代码块的正确方法是什么?
我想这样使用:
slice := []string{"a", "b", "c"}
newSlice := slice.Map(func(s string) string {
return s + "122"
})
您必须使用要用于 Map()
(或任何其他)方法的类型参数来声明 slice
类型:
type slice[E, V any] []E
并且您必须将类型参数添加到接收器,没有约束(这些将与类型声明中定义的相同),就好像您要实例化通用 slice
类型一样:
func (s *slice[E, V]) Map(iteratee func(E) V) *[]V {
result := []V{}
for _, item := range *s {
result = append(result, iteratee(item))
}
return &result
}
正在测试:
s := slice[int, string]{1, 2, 3}
m := s.Map(func(i int) string { return fmt.Sprint("x", i) })
fmt.Println(m)
将输出(在 Go Playground 上尝试):
&[x1 x2 x3]
另请注意,切片(切片 headers)已经包含指向底层数组的指针,因此您很少需要使用指向切片的指针。而是使用 non-pointer 接收器和 return 一个 non-pointer 声明方法(在 Go Playground 上尝试这个):
func (s slice[E, V]) Map(iteratee func(E) V) []V {
result := []V{}
for _, item := range s {
result = append(result, iteratee(item))
}
return result
}
(提示)规范中的相关部分:Spec: Method declarations:
If the receiver base type is a parameterized type, the receiver specification must declare corresponding type parameters for the method to use. This makes the receiver type parameters available to the method.
Syntactically, this type parameter declaration looks like an instantiation of the receiver base type, except that the type arguments are the type parameters being declared, one for each type parameter of the receiver base type. The type parameter names do not need to match their corresponding parameter names in the receiver base type definition, and all non-blank parameter names must be unique in the receiver parameter section and the method signature. The receiver type parameter constraints are implied by the receiver base type definition: corresponding type parameters have corresponding constraints.
方法不允许具有未在接收器类型上指定的类型参数。
原因是 Go 中的接口是隐式实现的,因此尚不清楚泛型方法如何实现接口。
为了使用第二个类型参数V
,它必须在类型声明中定义。例如:
// type def: must specify V here
type Slice[T, V any] []T
func (s Slice[T,V]) Map(iteratee func(T) V) []V {
result := make([]V, 0)
for _, item := range s {
result = append(result, iteratee(item))
}
return result
}
但是您也可以考虑使用通用的顶级函数,它具有允许 类型推断 的优点,因此可以减少冗长的代码并省略显式类型参数在实例化期间。
请注意,接收方必须重复类型参数声明,以便类型参数名称在方法的范围内:
func (s Slice[T,V]) Map(...)
如果某些方法根本不使用类型参数,您可以使用空白标识符 _
(下划线)来表示:
func (s Slice[T,_]) Foo( /* not using V herein */ )
然而,您的问题实际上是关于在未命名类型上定义泛型方法。
有或没有泛型都无法做到这一点。您只能在 defined types 上声明方法(即带有标识符的类型),并且在您的第二个片段中,E
不是定义的类型,它是一个 类型参数 。
与 Javascript 不同,其中所有数组都继承了 Array
原型,切片类型本身在 Go 中也没有定义。它在语言规范中形式化为:
SliceType = "[" "]" ElementType .
如您所见,没有标识符。所以你不能在任何和所有切片上声明一个方法,因为没有一个包罗万象的类型可以作为这种方法的接收者。
Playground 显示方法 vs func:https://gotipplay.golang.org/p/R7uAq0SBUjj
Golang 1.18beta 支持泛型,我想在泛型切片上添加扩展方法。例如地图函数定义如下:
func Map[E, V any](slice *[]E, iteratee func(E) V) *[]V {
result := []V{}
for _, item := range *slice {
result = append(result, iteratee(item))
}
return &result
}
然后想把这个方法做成slice的扩展方法,类似这样,但是编译不成功:
func (slice *[]E) Map[E, V any](iteratee func(E) V) *[]V {
result := []V{}
for _, item := range *slice {
result = append(result, iteratee(item))
}
return &result
}
go build
与 Go 1.18 给出错误:
main.go: method must have no type parameters
实现第二个代码块的正确方法是什么?
我想这样使用:
slice := []string{"a", "b", "c"}
newSlice := slice.Map(func(s string) string {
return s + "122"
})
您必须使用要用于 Map()
(或任何其他)方法的类型参数来声明 slice
类型:
type slice[E, V any] []E
并且您必须将类型参数添加到接收器,没有约束(这些将与类型声明中定义的相同),就好像您要实例化通用 slice
类型一样:
func (s *slice[E, V]) Map(iteratee func(E) V) *[]V {
result := []V{}
for _, item := range *s {
result = append(result, iteratee(item))
}
return &result
}
正在测试:
s := slice[int, string]{1, 2, 3}
m := s.Map(func(i int) string { return fmt.Sprint("x", i) })
fmt.Println(m)
将输出(在 Go Playground 上尝试):
&[x1 x2 x3]
另请注意,切片(切片 headers)已经包含指向底层数组的指针,因此您很少需要使用指向切片的指针。而是使用 non-pointer 接收器和 return 一个 non-pointer 声明方法(在 Go Playground 上尝试这个):
func (s slice[E, V]) Map(iteratee func(E) V) []V {
result := []V{}
for _, item := range s {
result = append(result, iteratee(item))
}
return result
}
(提示)规范中的相关部分:Spec: Method declarations:
If the receiver base type is a parameterized type, the receiver specification must declare corresponding type parameters for the method to use. This makes the receiver type parameters available to the method.
Syntactically, this type parameter declaration looks like an instantiation of the receiver base type, except that the type arguments are the type parameters being declared, one for each type parameter of the receiver base type. The type parameter names do not need to match their corresponding parameter names in the receiver base type definition, and all non-blank parameter names must be unique in the receiver parameter section and the method signature. The receiver type parameter constraints are implied by the receiver base type definition: corresponding type parameters have corresponding constraints.
方法不允许具有未在接收器类型上指定的类型参数。
原因是 Go 中的接口是隐式实现的,因此尚不清楚泛型方法如何实现接口。
为了使用第二个类型参数V
,它必须在类型声明中定义。例如:
// type def: must specify V here
type Slice[T, V any] []T
func (s Slice[T,V]) Map(iteratee func(T) V) []V {
result := make([]V, 0)
for _, item := range s {
result = append(result, iteratee(item))
}
return result
}
但是您也可以考虑使用通用的顶级函数,它具有允许 类型推断 的优点,因此可以减少冗长的代码并省略显式类型参数在实例化期间。
请注意,接收方必须重复类型参数声明,以便类型参数名称在方法的范围内:
func (s Slice[T,V]) Map(...)
如果某些方法根本不使用类型参数,您可以使用空白标识符 _
(下划线)来表示:
func (s Slice[T,_]) Foo( /* not using V herein */ )
然而,您的问题实际上是关于在未命名类型上定义泛型方法。
有或没有泛型都无法做到这一点。您只能在 defined types 上声明方法(即带有标识符的类型),并且在您的第二个片段中,E
不是定义的类型,它是一个 类型参数 。
与 Javascript 不同,其中所有数组都继承了 Array
原型,切片类型本身在 Go 中也没有定义。它在语言规范中形式化为:
SliceType = "[" "]" ElementType .
如您所见,没有标识符。所以你不能在任何和所有切片上声明一个方法,因为没有一个包罗万象的类型可以作为这种方法的接收者。
Playground 显示方法 vs func:https://gotipplay.golang.org/p/R7uAq0SBUjj