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.
CodePudding user response:
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)
}