我刚刚开始学习泛型.因此,我正在try 为我的定制数据库提供一个在一些协议消息上操作的驱动程序.

我想找到一种方法来进一步约束我的泛型类型,但将其作为指针,即确保(告诉编译器)约束E实现了另一个方法.

首先,我限制了db可以处理的实体.

type Entity interface {
   pb.MsgA | pb.MsgB | pb.MsgC
}

然后,编写了一个描述数据库功能的通用接口,以便它可以被处理各自的Proto消息的不同服务使用:

type DB[E Entity] interface {
   Get(...) (E, error)
   List(...) ([]E, error)
   ...
}

到目前一切尚好.然而,我也希望这些实体在与数据库通信时被(反)序列化,以有线方式发送、克隆和合并.大概是这样的:

func Encode[E Entity](v *E) ([]byte, error) {
   return proto.Marshal(v)
}

但是,上面的代码给出了以下错误:

cannot use val (variable of type *E) as type protoreflect.ProtoMessage in argument to proto.Marshal: *E does not implement protoreflect.ProtoMessage (type *E is pointer to type parameter, not type parameter)

问题是proto.Marshal需要实体(*E)实现proto.Message接口,即ProtoReflect()方法,我的所有实体类型都实现了这一接口,但它不受约束,编译器无法推断.

我还试图将实体定义为:

type Entity interface {
   *pb.MsgA | *pb.MsgB | *pb.MsgC
   proto.Message
}

但是,除了需要执行一些额外的前瞻性操作来实例化我的Proto.Messages实体指针之外,这样做感觉并不正确.

推荐答案

您可以这样做:

func Encode[M interface { *E; proto.Message }, E Entity](v M) ([]byte, error) {
    return proto.Marshal(v)
}

如果您想要比上面更干净的语法,您可以go 掉消息约束:

type Message[E Entity] interface {
    *E
    proto.Message
}

func Encode[M Message[E], E Entity](v M) ([]byte, error) {
    return proto.Marshal(v)
}

https://go.dev/play/p/AYmSKYCfZ1_l

Go相关问答推荐

链自定义GRPC客户端拦截器/DialOptions

如何配置vscode以在Go中显示不必要的(过度指定的)泛型?

如何用';贪婪原则';正确地

如何解析Go-Gin多部分请求中的 struct 切片

迭代字符串并用映射值替换原始字符串中的值的惯用方法

为什么 `go mod` 占用了另一个磁盘上的空间而不是我的 GOPATH?

不接受来自 stdin 的重复输入

通过 Terraform 中的 MapNestedAtribute 进行迭代

如何在正则表达式中使整个单词可选?

使用 httptest 对 http 请求进行单元测试重试

当我的 go build 成功时,如何修复我的 docker build 失败? Dockerfile 包括 go mod 下载

Golang - POST 失败(NoSurf CSRF)

AddE 上的 Apache Tinkerpop gremlin-go 驱动程序 Next() 返回E0903:没有剩余结果

Go泛型:无效的复合文字

从动态输入中提取字符串,其中部分字符串可能不存在

golang jwt.MapClaims 获取用户ID

有没有办法在golang中映射一组对象?

我应该明确地创建一个与Belongs To或Has Many对称的关系吗?

Go 泛型是否与 LINQ to Objects 等效?

Golang 查询扫描未将查询正确扫描到 struct 中