3

I am currently using the go-cmp package to compare struct equality. For testing purposes I have the need to compare two different types of structs that should have the same fields with the same values.

As a minimal example I am running into the issue where the cmp.Equal() function returns false for different types, even though they have the same fields and values.

type s1 struct {
    Name string
}

type s2 struct {
    Name string
}

p1 := s1{Name: "John"}
p2 := s2{Name: "John"}

fmt.Println(cmp.Equal(p1, p2)) // false

This is understandable since the two types are different but is there a way to instruct cmp.Equal() to ignore the types and only look at fields?

Loupi
  • 550
  • 6
  • 14

3 Answers3

2

I don't know if you can omit types during comparison, but if 2 struct types have identical fields, you can convert one to the other type, so this won't be an issue:

p1 := s1{Name: "John"}
p2 := s2{Name: "John"}

fmt.Println(cmp.Equal(p1, p2)) // false
fmt.Println(cmp.Equal(p1, s1(p2))) // true

Try it on the Go Playground.

icza
  • 389,944
  • 63
  • 907
  • 827
  • In this case converting one type to another does not work for me. One type comes from an external library and it contains unexported fields, and the other is one of my own. – Loupi Aug 07 '22 at 09:31
0

What I would suggest for long term, is to have a function IsS1EqualToS2 and check the fields one-by-one:

func IsS1EqualToS2(s1 s1, s2 s2) bool {
    if s1.Name != s2.Name {
        return false
    }
    return true
}

and use as: IsS1EqualToS2(p1, p2)

Davud Safarov
  • 498
  • 4
  • 12
  • `int(1)` and `string("1")` have the same string representation. – icza Aug 04 '22 at 15:54
  • 1
    @icza that is correct, but isn't the question about 2 structs that share the exact same fields in the same order? 1st solution(string equality) is an alternative way of checking the equality instead of doing `cmp.Equal(p1, s1(p2))`. If a field is an integer in one struct and string in another struct, then the question is no longer valid anyway. Because it specifically asks about 2 structs with the same fields – Davud Safarov Aug 04 '22 at 15:59
  • What if the struct has a field of type `[]any`, first struct holding `{1, "1"}`, and the other `{"1", 1}`? Your method claims them as equal, even though they're not. This is just one example of many fallpits of comparing string representations. – icza Aug 04 '22 at 16:04
  • my 1st answer is for 2 structs called s1 and s2 which have `Name string` field. As I wrote in the answer, better way of doing it is creating a function like `IsS1EqualToS2`, which would make it easy to compare S1 and S2 as the application grows and they have different structure. But I am sure that the 1st answer is what OP was looking for. Quick way to check the equality of **specifically** S1 and S2 structs – Davud Safarov Aug 04 '22 at 16:07
  • I believe `s1` and `s2` were just examples for the [mcve] and not the asker's real types, but @Loupi can correct me if I'm wrong. – icza Aug 04 '22 at 16:17
  • ah, i see. then it makes sense. But as long as the structs don't use generic fields, i don't think the string equality will give incorrect answer. But it might still cause confusion, so I will remove that part from the answer. thanks – Davud Safarov Aug 04 '22 at 16:20
0

Here is one way to compare fields without their type through json.Marshal and json.Unmarshal to interface type.

    type s1 struct {
        Name string
    }

    type s2 struct {
        Name string
    }

    p1 := s1{Name: "John"}
    p2 := s2{Name: "John"}

    fmt.Println(cmp.Equal(p1, p2)) // false

    var x1 interface{}
    b1, _ := json.Marshal(p1)
    _ = json.Unmarshal(b1, &x1)

    var x2 interface{}
    b2, _ := json.Marshal(p2)
    _ = json.Unmarshal(b2, &x2)

    fmt.Println(cmp.Equal(x1, x2)) // true

Playground sample

zangw
  • 43,869
  • 19
  • 177
  • 214