英文:
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
未定义(类型T
没有名为some
的字段或方法)
为什么会这样?如果我不能使用类型 *A
和 *B
的共享方法,那么定义 *A | *B
类型联合有什么意义呢?
(显然,我可以定义一个具有共享方法的接口,并直接使用它。但在我的特定用例中,我想明确限制为特定的类型。)
英文:
I'm trying to understand the usage of the type union constraint in Go generics (v1.18). Here is the code I tried:
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
}
The compiler complains:
> x.some
undefined (type T
has no field or method some)
Why is that? If I cannot use a shared method of type *A
and *B
, what's the point of defining types union *A | *B
at all?
(Apparently I can define an interface with the shared method and directly use that. But in my particular use case I want to restrict to the certain types explicitly.)
答案1
得分: 11
将方法添加到接口约束中,而不放弃泛型:
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的一个限制。令人困惑的是,语言规范似乎仍然支持你的理论(方法集):
接口类型的方法集是接口类型集合中每个类型的方法集的交集(通常结果方法集只是接口中声明的方法集)。
这个限制似乎只在Go 1.18发布说明中有记录:
当前的泛型实现有以下限制:
[...]
如果方法m
是由类型参数类型P
的约束接口显式声明的,那么Go编译器目前只支持在类型参数类型P
的值x
上调用方法m
。即使m
可能通过P
的方法集来实现。我们希望在Go 1.19中解除这个限制。
Go跟踪器中的相关问题是#51183,其中包括Griesemer的确认和决定保留语言规范,并记录这个限制。
英文:
Add the method to the interface constraint, without forgoing generics:
type AB interface {
*A | *B
some() bool
}
func some[T AB](x T) bool {
return x.some() // works
}
This restricts T
to types that are either *A
or *B
and declare some() bool
method.
However, as you already found out, this is a workaround. You are right that it should work with the type union alone. It's a limitation of Go 1.18. The confusing part is that the language specifications still seem to support your theory (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).
This limitation appears to be documented only in the 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.
The relevant issue in the Go tracker is #51183, with Griesemer's confirmation and the decision to leave the language specifications as is, and document the restriction.
答案2
得分: 4
将AB
的声明更改为:
type AB interface {
*A | *B
some() bool
}
在泛型Go中,约束是接口。如果类型参数实现了其约束,则该类型参数是有效的。
请观看有关泛型的Gophercon视频以更好地理解:
- Gophercon 2021: Robert Griesemer & Ian Lance Taylor - Generics!
- Gophercon 2020: Robert Griesemer - Typing [Generic] Go
为了确保我理解了你的问题,请在Go Playground的“Go Dev分支”模式中运行下面的代码片段:
// 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))
}
请在Go Playground中运行代码以查看结果。
英文:
Change the declaration of AB
to
type AB interface {
*A | *B
some() bool
}
In Generic Go, constraints are interfaces. A type argument is valid if it implements its constraints.
Please watch the Gophercon videos on Generics for a better understanding:
- Gophercon 2021: Robert Griesemer & Ian Lance Taylor - Generics!
- Gophercon 2020: Robert Griesemer - Typing [Generic] Go
To ensure that I understood your question please run the code snippet below in 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))
}
答案3
得分: 1
我认为旧的interface{}已经足够完成这个任务。
像这样:
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()
}
英文:
I think the old interface{} is enough to do this.
Like this:
type AB interface {
some() bool
}
But if you want to use generic, you must change the type first.
Like this:
func some[T AB](x T) bool {
if a, ok := interface{}(x).(*A); ok {
return a.some()
}
return (*B)(x).some()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论