I have a map that uses an interface as the key. The map is defined like this MyMap map[Signature]Packets
. The interface is Signature
, and there will be two structs A
and B
that implement this interface. I am also using msgp
to serialize these two structs.
My issue is that msgp
automatically generates methods that use a pointer as the type of the function receiver, which I think will make the key Signature
receive pointers. If that was the case, then the key would be different every single time since pointers are different, even though the underlying values are the same. So, every time, I would be creating a new entry instead of finding the existing one and modifying it.
I wonder:
- Is there a way to force
msgp
to generate methods purely with function receivers of the concrete type? Currently, I can only modify the function receivers of auto-generated methods likeMarshalMsg
andUnmarshalMsg
to the concrete type (A
orB
instead of*A
or*B
). By doing that, the key of the map is either of typeA
or of typeB
, and the mapMyMap
works fine. However, I know I should not modify the auto-generated code. So, I wonder whether there is an acceptable way to do that. - If there is no way to do 1., is there any workaround to solve this problem? I really need some polymorphic feature of the map's key with the use of
msgp
.
UPDATE 1 (Apr. 12): Thanks for sharing your thoughts and offering solutions. Here are some details about my question.
- The background is that the map is used for collecting different network events. The two structs implementing the interface
Signature
areEventSignatureIPv4
andEventSignatureIPv6
type EventSignatureIPv4 struct {
SourceIPv4 [4]byte
Port uint16
Traffic TrafficType
}
type EventSignatureIPv6 struct {
SourceIPv6 [16]byte
Port uint16
Traffic TrafficType
}
and Signature
is holding common methods shared between IPv4 and IPv6 data. So, essentially, I want to collect and group corresponding IPv4/v6 events at the runtime. The key of the map is to identify the same source, and the value of the map is to collect events with different destinations.
The
msgp
library I am using is this one https://pkg.go.dev/github.com/tinylib/[email protected]/msgpCorrect me if I am wrong. For compositions in Go, if one of the methods in the method set has a function receiver of the pointer type, then the instance would only be of the pointer type? So here, as I have
func (z *EventSignatureIPv6) MarshalMsg(b []byte) (o []byte, err error) {
/* Auto-generated code */
}
whenever I use Signature
to receive the struct EventSignatureIPv6
, the struct would only be of type *EventSignatureIPv6
?
You are right, "two pointer values are equal if they point to the same variable.", so if you are looking to compare interfaces that may hold pointers to different types, e.g.
*A
and*B
, you are already in trouble.With that said, I don't think it's an amazing idea to use interface types as map keys in the first place, because you have to deal with some caveats, the first is that:
And now you need to be careful about the types that implement the interface. In theory, nobody stops a client from implementing your interface on a defined type with underlying unhashable type, e.g.
type UncomparableSignature []int
So you would probably have to add an unexported method on your interface, so that client code outside that package can't implement it. But still, nothing stops code within the same package from implementing it, so this is, at best, maintenance overhead.
Then if the interface holds pointers to zero-values, it's even dependant on the implementation of the specs:
Furthermore, you open yourself up to pesky bugs, like variables of type
Signature
that holds anil
will overwrite each other's values:A possible solution is, you could replace the map key with a unique id, and you enforce implementors to provide it by declaring the appropriate method on the interface
Signature
(this still assumes that the implementors can be coordinated to provide unique ids across all of them):and then