有没有办法使用泛型来确保传递的值具有特定的字段?

huangapple go评论103阅读模式
英文:

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

huangapple
  • 本文由 发表于 2022年3月6日 03:17:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/71365287.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定