I've been using reflect.DeepEqual
to deeply compare structs using circular pointers. As that does not work with maps and for better test output, I've switched to go-cmp
.
Now I had to take note that even though cmp.Equal
is supposed to be a drop-in replacement for reflect.DeepEqual
, in cases where the former works correctly, there's a different result in this case, even though it actually is deeply equal.
Can anyone tell me why the result is different in this case and ideally, how to fix it?
Code in Go playground: https://play.golang.com/p/rLWKwMlAfwu
(Updated to use fmt.Printf(), because I couldn't get testing
running in playground)
Output for diff:
StrongConnect() mismatch (-want got):
&⟪ref#0⟫main.Edge{
StartNode: &⟪ref#1⟫main.Node{
Variable: 1,
- Low: &⟪ref#0: 0xc00005c120⟫(...),
Low: &⟪ref#0: 0xc00005c120⟫(...),
High: &{StartNode: &⟪ref#1⟫(...), EndNode: &{Variable: 2}, EdgeType: 1, Weight: 1},
},
EndNode: &{Variable: 2},
EdgeType: 0,
Weight: 1,
}
CodePudding user response:
reflect.DeepEqual
is more lax than cmp.Equal
when comparing structures with cycles (and arguably incorrect).
cmp.Equal
will only consider overlapping graphs equivalent if the set of nodes and edges in the graph is the same. Note: Both Node
and Edge
structs are nodes in this graph comparison.
In your example, the 2 graphs/structs overlap but wantEdge0
is has an extra Edge
struct as the root node (prepended).
Here is cut down representation of the cycles in your data structures:
wantEdge0 := &main.Edge{ // n0
StartNode: &main.Node{ // n1
Low: &main.Edge{} // n2
},
}
wantEdge0.StartNode.Low.StartNode = wantEdge0.StartNode // n1
got := wantEdge0.StartNode.Low // n2
Hence there are the 2 different cycles:
wantEdge0 [n0] -> wantEdge0.StartNode [n1] -> got [n2] -> wantEdge0.StartNode [n1]
got [n2] -> wantEdge0.StartNode [n1] -> got [n2]
Here is a simple example that demonstrates this difference between reflect.DeepEqual
and cmp.Equal
:
package main
import (
"fmt"
"reflect"
"github.com/google/go-cmp/cmp"
)
type Node struct {
Next *Node
Value int
}
func main() {
a0 := &Node{}
a1 := &Node{}
a2 := &Node{}
a0.Next = a1
a1.Next = a2
a2.Next = a1
b1 := &Node{}
b2 := &Node{}
b1.Next = b2
b2.Next = b1
fmt.Println("DeepEqual\tcmp.Equal")
fmt.Printf("\t%v\t%v\t\tIndependent graphs\n", reflect.DeepEqual(a1, b1), cmp.Equal(a1, b1))
fmt.Printf("\t%v\t%v\t\tSame graph, different root\n", reflect.DeepEqual(a1, a2), cmp.Equal(a1, a2))
fmt.Printf("\t%v\t%v\t\tSame graph prepend vs no prepend\n", reflect.DeepEqual(a0, a1), cmp.Equal(a0, a1))
}
Output:
$ ./compare
DeepEqual cmp.Equal
true true Independent graphs
true true Same graph, different root
true false Same graph prepend vs no prepend
Solution: I would recommend allocating completely separate structures for your want struct and the test input. This way the want and got structs won't overlap and they should compare as intended.
References: