3

I have a Favorites struct with a slice field:

type Favorites struct {
    Color string
    Lunch string
    Place string
    Hobbies []string 
}

and I have a Person which contains the other struct:

type Person struct {
    Name string
    Favorites Favorites
}

I'd like to see if the Favorites field is set on Person. For other types of fields, for example, a string or an int, I would compare that field to the zero value ("" or 0 respectively).

If I try to compare with the zero as below I get the error invalid operation: p2.Favorites == zeroValue (struct containing []string cannot be compared):

p2 := Person{Name: "Joe"}

zeroValue := Favorites{}
if p2.Favorites == zeroValue {
    fmt.Println("Favorites not set")
}

This matches what is defined in the spec (https://golang.org/ref/spec#Comparison_operators).

Is there anyway to do this comparison other than tediously comparing every single field (and having to remember to update it if the struct changes)?

One option is to make the Favorites field a pointer to the struct instead of the struct itself and then just compare with nil, but this is in a large codebase so I'd rather not make potentially far-reaching changes in this case.

https://play.golang.org/p/d0NSp8eBes

stephenbez
  • 5,598
  • 3
  • 26
  • 31
  • Can you explain why you prefer not to make the `Favorites` field a pointer? If you don't care about equivalence and are simply trying to verify the field is set at all, then this seems like the best option. – dalton_c Dec 22 '16 at 20:54
  • 1
    To add to my previous comment, I would suggest being careful about using the zero value to denote "un-set" on other types as well. 0 may be a perfectly valid value for an `int` field. – dalton_c Dec 22 '16 at 20:57
  • @daltonclaybrook It would usually be the best option. In a large codebase where the struct is used all over, I would be more concerned with making the change. Also if there are many struct fields in the struct, it seems like if I changed one to be a pointer, then for consistency I should probably change all of them. – stephenbez Dec 22 '16 at 21:11

1 Answers1

5

According to this, you can use reflect.DeepEqual(), but probably should just write your own:

type Favorites struct {
    Color string
    Lunch string
    Place string
    Hobbies []string 
}

func (favs *Favorites) Equals(other *Favorites) bool {
    color_eq := favs.Color == other.Color
    lunch_eq := favs.Lunch == other.Lunch
    place_eq := favs.Place == other.Place
    hobbies_eq := len(favs.Hobbies) == len(other.Hobbies)
    if hobbies_eq {  // copy slices so sorting won't affect original structs
        f_hobbies := make([]string, len(favs.Hobbies))
        o_hobbies := make([]string, len(other.Hobbies))
        copy(favs.Hobbies, f_hobbies)
        copy(other.Hobbies, o_hobbies)
        sort.Strings(f_hobbies)
        sort.Strings(o_hobbies)
        for index, item := range f_hobbies {
            if item != o_hobbies[index] {
                hobbies_eq = false
            }
        }
    }
    return (color_eq && lunch_eq && place_eq && hobbies_eq)
}

Then call it with:

p2.Favorites.Equals(zeroValue)
Community
  • 1
  • 1
Will
  • 4,299
  • 5
  • 32
  • 50