前往:界面类型映射中的比较

Let's say I have a lot of different struct types which all satisfy an interface, Food.

type Food interface {
    Name() string
    Tastiness() int
}

type fruit struct {
    species  string
    numSeeds int
}

type vegetable struct {
    commonName string
    weight  int
}

func (f *fruit) Name() string       { return f.species }
func (f *fruit) Tastiness() int     { return 100 - f.numSeeds }
func (v *vegetable) Name() string   { return v.commonName }
func (v *vegetable) Tastiness() int { return 1 }

The structs that satisfy the Food interface do so with functions that are pointer receivers because I pass them around often, which is unwieldy without pointers.

Often, I want to make comparisons between food1 and food2, and so I construct maps that look like foodmap := map[Food]bool. What I really want to check is if the underlying structs are the same thing. But, because it's always pointers that are satisfying the interface, I can't do equality or presence tests using maps or lists if I create two of the same kind of fruit or vegetable, for example.

Is the best way to compare Foods to not use map[Food]bool and instead use something like map[FoodKey], where any struct that satsifies Food provides a FoodKey() comparisonStruct method that returns a struct meant strictly for comparison?

Is the best way to compare Foods to not use map[Food]bool and instead use something like map[FoodKey], where any struct that satsifies Food provides a FoodKey() comparisonStruct method that returns a struct meant strictly for comparison?

I suspect it is a better approach, considering:

You have two options to determine or compare the underlying structs of interfaces.

1- Use the reflect package, specifically the reflect.TypeOf function, which will return the dynamic type of the interface, such as:

x := Food(fruit{"banana", 0})
y := Food(vegetable{"potato, 45})
return reflect.TypeOf(x) == reflect.TypeOf(y)

This piece of code will return false.

2- Use type assertions or a type switch, such as:

value, ok := yourInterface.(possibleType)

I believe the most efficient path here is to add an extra function to your interface like Is(f Food) bool, it's easy to implement, no overhead of using reflection or comparing interfaces or using a map somewhere.

Example:

type Food interface {
    Name() string
    Tastiness() int
    Is(f Food) bool
}
//....
func (*fruit) Is(f Food) bool   { _, ok := f.(*fruit); return ok }
//....
func (*vegetable) Is(f Food) bool   { _, ok := f.(*vegetable); return ok }

playground