英文:
Is there a way to constraint (generic) type parameters?
问题
我刚开始学习泛型。所以我正在尝试为我的自定义数据库编写一个通用的驱动程序,用于处理一些 protobuf 消息。
我想找到一种方法来进一步约束我的泛型类型,但是作为一个指针,也就是说,确保(告诉编译器)约束 E 实现了另一个方法。
首先,我约束了 db 可以处理的实体。
type Entity interface {
pb.MsgA | pb.MsgB | pb.MsgC
}
然后,编写了一个通用接口来描述 db 的功能,以便可以由处理相应 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()
方法,而我所有的 Entity 类型确实都实现了它,但它没有被约束,编译器无法推断。
我还尝试将实体定义为:
type Entity interface {
*pb.MsgA | *pb.MsgB | *pb.MsgC
proto.Message
}
然而,这种方式不太对,而且我需要进行一些额外的 protreflect 操作来实例化我的 proto.Messages,而实体指针引用了它。
英文:
I've just started to learn about generics. So I'm trying to generalize a driver for my custom database operating on some protobuf messages.
I want to find a way to further constraint my generic type but as a pointer, i.e. make sure (tell the compiler) that constraint E implements another method.
First, I've constrained what entities db can handle.
type Entity interface {
pb.MsgA | pb.MsgB | pb.MsgC
}
Then, wrote a generic interface describing db capabilities so it can be used by different services handling respective proto messages:
type DB[E Entity] interface {
Get(...) (E, error)
List(...) ([]E, error)
...
}
So far so good. However, I also want these entities to be (de)serialized to be sent on wire, cloned and merged when communicating with the database. Something like this:
func Encode[E Entity](v *E) ([]byte, error) {
return proto.Marshal(v)
}
However, the code above gives me the following error:
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)
The problem is proto.Marshal
requires entity (*E) to implement proto.Message
interface, namely ProtoReflect()
method, which all my Entity types do implement, but it is not constrained and compiler cannot infer.
I have also tried to define entity as:
type Entity interface {
*pb.MsgA | *pb.MsgB | *pb.MsgC
proto.Message
}
However, that doesn't feel right in addition to I need to do some extra protreflect operations to instantiate my proto.Messages which Entity pointer reference to.
答案1
得分: 3
你可以这样做:
func Encode[M interface { *E; proto.Message }, E Entity](v M) ([]byte, error) {
return proto.Marshal(v)
}
如果你想要比上面更简洁的语法,你可以声明消息约束:
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
英文:
You can do something like this:
func Encode[M interface { *E; proto.Message }, E Entity](v M) ([]byte, error) {
return proto.Marshal(v)
}
If you'd like a more cleaner syntax than the above, you can delcare the message constraint:
type Message[E Entity] interface {
*E
proto.Message
}
func Encode[M Message[E], E Entity](v M) ([]byte, error) {
return proto.Marshal(v)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论