跟随泛型:type *T 是类型参数的指针,而不是类型参数。

12

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

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

我想为不同类型(这里是 A 和 B)实现一个对象存储,它们共享一个公共的 ID 字段。根据 DRY 的思想,我想使用泛型来实现存储。

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

./prog.go:29:7: item.SetId 未定义(类型 *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())
}
1个回答

21

关于使用*T

简而言之,类型参数并不是其约束条件。约束条件只确定了在T上可用的操作,它并不意味着任何关于*T的内容,这里*T只是一个未命名的指针类型。这就是以下语句的含义:

type *T is pointer to type parameter, not type parameter

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

您需要通过设置额外的约束条件来明确告诉编译器。简化形式如下:

func Foo[T any, PT interface { SetId(string); *T}](v T) {}

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

关于实现约束

这个实例化&MyStore[A]{}失败的原因在错误消息中得到了明确的报告:

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

换句话说,SetId()*A上声明,而不是A。 因此,您应该使用*A实例化MyStore

var storeA = &MyStore[*A]{}

接下来将结构体/方法定义中的*T替换成T

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

func (s *MyStore[T]) add(item T) {
}

使用*A实例化后,该字段的类型将变为map[string]*A,因此可以有效地进行赋值storeA.values = make(map[string]*A),并且方法签名变为add(item *A),因此可以允许storeA.add(&A{})
修复的游乐场:https://gotipplay.golang.org/p/dcUVJ5YQK_b

不错!此外,我认为将其更改为(*item).SetId("aa")也可以解决第一个问题。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接