为什么 go-cmp Equal() 说结构不是深度相等,即使所有字段都深度相等?

Why does go-cmp Equal() say that the structs are not deeply equal, even though all fields are deeply equal?

我一直在使用 reflect.DeepEqual 来深入比较使用循环指针的结构。由于这不适用于地图和更好的测试输出,我切换到 go-cmp.

现在我必须注意,即使 cmp.Equal 应该是 reflect.DeepEqual 的直接替代品,但在前者正常工作的情况下,在这种情况下会有不同的结果,即使它实际上非常相等。

任何人都可以告诉我为什么在这种情况下结果不同,理想情况下如何解决?

Go playground 中的代码:https://play.golang.com/p/rLWKwMlAfwu (更新为使用 fmt.Printf(),因为我无法在操场上获得 testing 运行)

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,
  }

reflect.DeepEqual is more lax than cmp.Equal 将结构与循环进行比较(并且可以说是不正确的)。

cmp.Equal 仅当图中的节点和边集相同时才认为重叠图等效。 注意NodeEdge结构都是该图比较中的节点。

在您的示例中,2 graphs/structs 重叠但 wantEdge0 有一个额外的 Edge 结构作为根节点(前置)。

这里是你的数据结构中循环的简化表示:

wantEdge0 := &main.Edge{   // n0
    StartNode: &main.Node{ // n1
        Low: &main.Edge{}  // n2
    },
}
wantEdge0.StartNode.Low.StartNode = wantEdge0.StartNode // n1
got := wantEdge0.StartNode.Low                          // n2

因此有 2 个不同的周期:

  1. wantEdge0 [n0] -> wantEdge0.StartNode [n1] -> got [n2] -> wantEdge0.StartNode [n1]
  2. got [n2] -> wantEdge0.StartNode [n1] -> got [n2]

这里有一个简单的例子来说明 reflect.DeepEqualcmp.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))
}

输出:

$ ./compare 
DeepEqual   cmp.Equal
    true    true        Independent graphs
    true    true        Same graph, different root
    true    false       Same graph prepend vs no prepend

解决方案:我建议为您的 want 结构和测试输入分配完全独立的结构。这样 wantgot 结构就不会重叠,它们应该按预期进行比较。

参考文献: