英文:
Is there a way we can ensure passed values have certain fields using generics?
问题
我正在尝试在Go语言中定义一个通用函数,该函数接受具有特定字段的值,例如ID int
。我尝试了几种方法,但似乎都不起作用。以下是我尝试的一个示例。
package main
import (
"fmt"
)
func Print[T IDer](s T) {
fmt.Print(s.ID)
}
func main() {
Print(Person{3, "Test"})
}
type IDer interface {
~struct{ ID int }
}
type Person struct {
ID int
Name string
}
type Store struct {
ID int
Domain string
}
这是Playground链接:https://gotipplay.golang.org/p/2I4RsUCwagF
在上面的示例中,我希望确保传递给Print
函数的每个值都具有一个名为ID int
的属性,并且在函数中可以访问该属性。在Go语言中,是否有任何方法可以实现这一点,而无需在接口中定义一个方法(例如GetID() int
)?
英文:
I am trying to define a generic function in Go that accepts values that have certain fields, for example, ID int
. I have tried several approaches but none seems to work. Here is an example of what I have tried.
package main
import (
"fmt"
)
func Print[T IDer](s T) {
fmt.Print(s.ID)
}
func main() {
Print(Person{3, "Test"})
}
type IDer interface {
~struct{ ID int }
}
type Person struct {
ID int
Name string
}
type Store struct {
ID int
Domain string
}
And here is the playground link: https://gotipplay.golang.org/p/2I4RsUCwagF
In the example above, I want to guarantee every value passed to the Print
function has a property ID int
, which is also accessible in the function. Is there any way I can achieve this in Go without defining a method in an interface (e.g., GetID() int
)?
答案1
得分: 6
在Go中,如果不在接口中定义方法(例如GetID() int),是没有办法实现这个功能的。
尽管Go 1.18中的泛型实现并不支持结构类型,即使最初的类型参数提案中建议支持。如果要访问联合中的公共字段,可以参考这个解释。
不过,我认为有必要指出一个容易从你的示例中产生的误解:近似类型~T
(波浪线类型)的含义是“底层类型为T的类型集合”。
现在,当你写下:
~struct{ ID int }
这意味着底层类型恰好是struct{ ID int }
的类型。无论如何,这都不包括具有字段ID int
和其他字段的结构体。例如,type Foo struct { ID int; Name string }
的底层类型是struct { ID int; Name string }
,而不是struct{ ID int }
,因此它无法满足约束条件。
当前的时间参数实现没有语法来指定部分结构类型。我记得有一个提案在接口约束中添加字段术语(以及类型术语和方法),类似于:
type IDer interface {
ID int
}
这将使你尝试的功能成为可能,而不会破坏波浪线~
的含义。但这不会包含在Go 1.18中。
英文:
> Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?
No, you have to define the method in an interface.
The generics implementation in Go 1.18 doesn't have support for structural types, even though the original type parameters proposal suggests it would. For accessing common fields in a union instead see also this explanation.
Although, I think it's worth it to point out a misconception that can easily arise from your example: the meaning of the approximation ~T
(tilde-type) means "the set of types whose underlying type is T.
Now, when you write:
~struct{ ID int }
this means types whose underlying type is exactly struct{ ID int }
. No matter what, this does not include structs that have the field ID int
and something else. E.g. the underlying type of type Foo struct { ID int; Name string }
is struct { ID int; Name string }
, and not struct{ ID int }
, so that wouldn't satisfy the constraint anyway.
The current time param implementation doesn't have syntax to specify partial struct types. I recall a proposal to add field terms in interface constraints (along with type terms and methods), something on the line:
type IDer interface {
ID int
}
which would enable what you are trying to do without breaking the meaning of the tilde ~
. But this won't be included in Go 1.18.
答案2
得分: 0
作为解决方法,您可以为字段提供getter方法。然后,您的类型限制可以使用具有该getter的接口。
您可以通过定义一个带有ID()
方法的WithID
结构体来轻松添加一个ID getter。以下是基于您的代码的完整示例:
package main
import (
"fmt"
)
type IDer interface {
ID() int
}
func Print[T IDer](s T) {
fmt.Print(s.ID())
}
type WithID int
func (id WithID) ID() int {
return int(id)
}
type Person struct {
WithID
Name string
}
type Store struct {
WithID
Domain string
}
func main() {
Print(Person{1, "Name"})
Print(Store{2, "Domain"})
}
Playground: https://gotipplay.golang.org/p/-sG8XQGnySS
英文:
As a workaround, you can provide getters for your fields. Then your type restriction can use an interface with that getter.
You can make it easy to add an ID getter by defining a WithID
struct with ID()
method. Here is a full example based on your code:
package main
import (
"fmt"
)
type IDer interface {
ID() int
}
func Print[T IDer](s T) {
fmt.Print(s.ID())
}
type WithID int
func (id WithID) ID() int {
return int(id)
}
type Person struct {
WithID
Name string
}
type Store struct {
WithID
Domain string
}
func main() {
Print(Person{1, "Name"})
Print(Store{2, "Domain"})
}
Playground: https://gotipplay.golang.org/p/-sG8XQGnySS
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论