在 Go 泛型中,如何在联合约束中对类型使用通用方法?
In Go generics, how to use a common method for types in a union constraint?
我正在尝试了解 Go 泛型 (v1.18) 中类型联合约束的用法。这是我试过的代码:
type A struct {
}
type B struct {
}
type AB interface {
*A | *B
}
func (a *A) some() bool {
return true
}
func (b *B) some() bool {
return false
}
func some[T AB](x T) bool {
return x.some() // <- error
}
编译器抱怨:
x.some
undefined (type T
has no field or method some)
这是为什么?如果我不能使用 *A
和 *B
类型的共享方法,那么定义类型 union *A | *B
有什么意义呢?
(显然我可以用共享方法定义一个接口并直接使用它。但在我的特定用例中我想明确地限制某些类型。)
我认为旧界面{}足以做到这一点。
像这样:
type AB interface {
some() bool
}
但是如果你想使用泛型,你必须先改变类型
像这样:
func some[T AB](x T) bool {
if a, ok := interface{}(x).(*A); ok {
return a.some()
}
return (*B)(x).some()
}
将 AB
的声明更改为
type AB interface {
*A | *B
some() bool
}
在 Generic Go 中,约束是接口。如果类型参数实现了它的约束,它就是有效的。
请观看有关泛型的 Gophercon 视频以更好地理解:
- Gophercon 2021: Robert Griesemer & Ian Lance Taylor - Generics!
- Gophercon 2020: Robert Griesemer - Typing [Generic] Go
为确保我理解您的问题,请 运行 下面 Go Playground in “Go Dev branch” mode 中的代码片段:
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
type A struct {
}
type B struct {
}
type C struct{}
type AB interface {
*A | *B
some() bool
}
func (a *A) some() bool {
return true
}
func (b *B) some() bool {
return false
}
func (c *C) some() bool {
return false
}
func some[T AB](x T) bool {
return x.some()
}
func main() {
p := new(A)
fmt.Println(some(p))
//uncomment the lines below to see that type C is not valid
//q := new(C)
//fmt.Println(some(q))
}
在不放弃泛型的情况下将方法添加到接口约束中:
type AB interface {
*A | *B
some() bool
}
func some[T AB](x T) bool {
return x.some() // works
}
这将 T
限制为 *A
或 *B
类型并声明 some() bool
方法。
但是,正如您已经发现的那样,这是一种解决方法。你是对的,它应该单独与类型联合一起工作。这是 Go 1.18 的限制。令人困惑的部分是语言规范似乎仍然支持你的理论 (Method sets):
The method set of an interface type is the intersection of the method sets of each type in the interface's type set (the resulting method set is usually just the set of declared methods in the interface).
此限制似乎仅记录在 Go 1.18 release notes:
The current generics implementation has the following limitations:
[...]
The Go compiler currently only supports calling a method m
on a value x
of type parameter type P
if m
is explicitly declared by P
's constraint interface. [...] even though m
might be in the method set of P
by virtue of the fact that all types in P
implement m
. We hope to remove this restriction in Go 1.19.
Go 跟踪器中的相关问题是 #51183, with Griesemer's confirmation 以及保留语言规范原样并记录限制的决定。
我正在尝试了解 Go 泛型 (v1.18) 中类型联合约束的用法。这是我试过的代码:
type A struct {
}
type B struct {
}
type AB interface {
*A | *B
}
func (a *A) some() bool {
return true
}
func (b *B) some() bool {
return false
}
func some[T AB](x T) bool {
return x.some() // <- error
}
编译器抱怨:
x.some
undefined (typeT
has no field or method some)
这是为什么?如果我不能使用 *A
和 *B
类型的共享方法,那么定义类型 union *A | *B
有什么意义呢?
(显然我可以用共享方法定义一个接口并直接使用它。但在我的特定用例中我想明确地限制某些类型。)
我认为旧界面{}足以做到这一点。
像这样:
type AB interface {
some() bool
}
但是如果你想使用泛型,你必须先改变类型
像这样:
func some[T AB](x T) bool {
if a, ok := interface{}(x).(*A); ok {
return a.some()
}
return (*B)(x).some()
}
将 AB
的声明更改为
type AB interface {
*A | *B
some() bool
}
在 Generic Go 中,约束是接口。如果类型参数实现了它的约束,它就是有效的。
请观看有关泛型的 Gophercon 视频以更好地理解:
- Gophercon 2021: Robert Griesemer & Ian Lance Taylor - Generics!
- Gophercon 2020: Robert Griesemer - Typing [Generic] Go
为确保我理解您的问题,请 运行 下面 Go Playground in “Go Dev branch” mode 中的代码片段:
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
type A struct {
}
type B struct {
}
type C struct{}
type AB interface {
*A | *B
some() bool
}
func (a *A) some() bool {
return true
}
func (b *B) some() bool {
return false
}
func (c *C) some() bool {
return false
}
func some[T AB](x T) bool {
return x.some()
}
func main() {
p := new(A)
fmt.Println(some(p))
//uncomment the lines below to see that type C is not valid
//q := new(C)
//fmt.Println(some(q))
}
在不放弃泛型的情况下将方法添加到接口约束中:
type AB interface {
*A | *B
some() bool
}
func some[T AB](x T) bool {
return x.some() // works
}
这将 T
限制为 *A
或 *B
类型并声明 some() bool
方法。
但是,正如您已经发现的那样,这是一种解决方法。你是对的,它应该单独与类型联合一起工作。这是 Go 1.18 的限制。令人困惑的部分是语言规范似乎仍然支持你的理论 (Method sets):
The method set of an interface type is the intersection of the method sets of each type in the interface's type set (the resulting method set is usually just the set of declared methods in the interface).
此限制似乎仅记录在 Go 1.18 release notes:
The current generics implementation has the following limitations:
[...] The Go compiler currently only supports calling a method
m
on a valuex
of type parameter typeP
ifm
is explicitly declared byP
's constraint interface. [...] even thoughm
might be in the method set ofP
by virtue of the fact that all types inP
implementm
. We hope to remove this restriction in Go 1.19.
Go 跟踪器中的相关问题是 #51183, with Griesemer's confirmation 以及保留语言规范原样并记录限制的决定。