使用泛型:类型 *T 是指向类型参数的指针,而不是类型参数

Mar*_*tin 14 generics go

可能是 golang 初学者的问题:)

尝试编译下面的代码时,我遇到以下编译器错误。

我想为共享公共 ID 字段的不同类型(此处为 A 和 B)实现一个对象存储。遵循 DRY 的想法,我想使用泛型来实现商店。

添加对象时,我想使用 GS 接口设置其 ID 字段(当然实际代码要复杂一点),但编译器不希望我这样做。

./prog.go:29:7: item.SetId undefined (类型 *T 是指向类型参数的指针,而不是类型参数)

./prog.go:34:24:A 未实现 GS(SetId 方法有指针接收器)

有推荐的方法来解决这个问题吗?提前致谢!!

package main

import "fmt"

type A struct {
    ID      string
    AMember string
}
type B struct {
    ID      string
    BMember string
}

type GS interface {
    Id() string
    SetId(string)
}

func (s A) Id() string      { return s.ID }
func (s *A) SetId(i string) { s.ID = i }
func (s B) Id() string      { return s.ID }
func (s *B) SetId(i string) { s.ID = i }

type MyStore[T GS] struct {
    values map[string]*T
}

func (s *MyStore[T]) add(item *T) {
    item.SetId("aa")
    s.values["aa"] = item
}

func main() {
    var storeA = &MyStore[A]{}
    storeA.values = make(map[string]*A)
    a := &A{}

    storeA.add(a)

    fmt.Println(a.Id())
}
Run Code Online (Sandbox Code Playgroud)

bla*_*een 26

关于使用 *T

简而言之,类型参数不是它的约束。该约束仅确定 上可以执行哪些操作T,它并不暗示关于 的任何操作*T,现在它只是一个未命名的指针类型。这就是:

type *T 是指向类型参数的指针,而不是类型参数

因此,正如您的情况一样, 的方法集*T不会自动包含在 的T具体类型上声明的指针接收器方法A,并且它不会实现将由 实现的接口*A

您必须通过设置额外的约束来向编译器明确这一点。以简化的形式来说,它会是这样的:

func Foo[T any, PT interface { SetId(string); *T}](v T) {}
Run Code Online (Sandbox Code Playgroud)

您可以在此处找到有关此用例的更广泛的示例和变体:

关于实施约束

&MyStore[A]{}错误消息清楚地报告了实例化失败的原因:

A没有实现GS(SetId方法有指针接收器)

换句话说,SetId()是在*A、 而不是 上声明的A。因此你应该实例MyStore*A

var storeA = &MyStore[*A]{}
Run Code Online (Sandbox Code Playgroud)

*T然后将结构/方法定义中出现的 更改为T

type MyStore[T GS] struct {
    values map[string]T // just T instead of *T
}

func (s *MyStore[T]) add(item T) {
}
Run Code Online (Sandbox Code Playgroud)

在实例化时,*A字段的类型将变得等效,从而map[string]*A使赋值storeA.values = make(map[string]*A)有效,并且方法签名add(item *A)因此允许storeA.add(&A{}).

固定游乐场:https://gotipplay.golang.org/p/dcUVJ5YQK_b