1.结构体中有未导出字段
type Address struct {
Province string
city string //未导出字段
}
type User2 struct {
Name string
Age int
Address Address
}
func main() {
u1 := User2{"dj", 18, Address{Province: "0", city: "1"}}
u2 := User2{"dj", 18, Address{Province: "0", city: "2"}}
fmt.Println("u1 equals u2?", cmp.Equal(u1.Address, u2.Address, cmpopts.IgnoreUnexported(Address{}))) //true
fmt.Println("u1 equals u2?", cmp.Equal(u1.Address, u2.Address, cmp.AllowUnexported(Address{}))) //false
//fmt.Println("u1 equals u2?", cmp.Equal(u1, u2))
//会直接panic,因为cmp.Equal()比较的两个结构体里面不能有未导出字段
//这种情况下,如果笼统使用cmpopts.IngoreUnexported(User2{})还是会panic,因为city准确来说是Address中的未导出字段,而非User2的直接字段。
fmt.Println("u1 equals u2?", cmp.Equal(u1, u2, cmpopts.IgnoreUnexported(Address{}))) //true
//cmpopts.IgnoreUnexported(Address{})可以在对比两个对象的时候,不进行Address{}的未导出字段对比
fmt.Println("u1 equals u2?", cmp.Equal(u1, u2, cmp.AllowUnexported(Address{}))) //false
//我们也可以使用cmdopts.AllowUnexported(Address{})表示需要比较Address的未导出字段:
}
2.结构体中字段都可导出且有指针变量
type Contact struct {
Phone string
Email string
}
type User struct {
Name string
Age int
Contact *Contact
}
func main() {
u1 := User{Name: "dj", Age: 18}
u2 := User{Name: "dj", Age: 18}
fmt.Println("u1 == u2?", u1 == u2) //true
fmt.Println("u1 equals u2?", cmp.Equal(u1, u2)) //true
c1 := &Contact{Phone: "123456789", Email: "dj@example.com"}
c2 := &Contact{Phone: "123456789", Email: "dj@example.com"}
u1.Contact = c1
u2.Contact = c2
fmt.Println("u1 == u2 with different pointer?", u1 == u2) //false【结构体里面带有指针的地址不同就不等】
fmt.Println("u1 equals u2 with different pointer?", cmp.Equal(u1, u2)) //true【不看地址,只关注值一不一样】
}
cmp.Equal()与reflect.DeepEqual的不同
默认情况下,cmp.Equal()函数不会比较未导出字段,因为会直接panic(字段名首字母小写的字段叫未导出字段)
遇到未导出字段,cmp.Equal()直接panic,这一点与reflect.DeepEqual()有所不同,后者会比较未导出的字段。
3.浮点数的比较
go-cmp提供比较浮点数的选项,两个浮点数相差不超过一定范围就认为它们相等:
cmpopts.EquateApprox(fraction, margin):
第一个参数的含义是取两个数绝对值的较小者,乘以fraction,如果两个数的差的绝对值小于这个数即|x-y| ≤ max(fraction*min(|x|, |y|), margin),则认为它们相等。
第二个参数比较好理解,如果两个浮点数的差的绝对值小于margin则认为它们相等。
如果fraction和margin同时设置,只需要满足一个就行了。
type FloatPair struct {
X float64
}
func main() {
f1 := 0.1
f2 := 0.2
f3 := 0.3
p3 := FloatPair{X: f1 + f2}
p4 := FloatPair{X: f3}
fmt.Printf("p3=%v p4=%v\n", p3, p4) //p3={0.30000000000000004} p4={0.3}【由于变量的存储空间,会存在误差】
fmt.Println("p3 == p4?", p3 == p4) //false
fmt.Println("p3 equals p4?", cmp.Equal(p3, p4)) //false
fmt.Println("p3 equals p4?", cmp.Equal(p3, p4, cmpopts.EquateApprox(0.1, 0.001))) //true
//一般情况下,满足第一个误差就可以了,因为 0.0..04 明显小于 0.1 所以 true
}
4. nil切片 与 空切片 有时候就是想要忽略他们的有无地址
- 默认情况下,如果一个切片变量值为nil,另一个是使用make创建的长度为 0 的切片,
- 那么go-cmp认为它们是不等的。
- 同样的,一个map变量值为nil,一个是使用make创建的长度为 0 的map,go-cmp也认为它们不等。
- 我们可以指定cmpopts.EquateEmpty选项,让go-cmp认为它们相等
func main() {
var s1 []int //没地址没值
var s2 = make([]int, 0) //有地址没值
var m1 map[int]int //没地址没值
var m2 = make(map[int]int) //有地址没值
//fmt.Println(s1==s2)//直接编译报错
//fmt.Println(m1==m2)//直接编译报
fmt.Println("s1 equals s2?", cmp.Equal(s1, s2))
fmt.Println("m1 equals m2?", cmp.Equal(m1, m2))
//相当于cmpopts.EquateEmpty()忽略了有无地址的不同
//没地址没值,和有地址没值本质上是不一样的
fmt.Println("s1 equals s2 with option?", cmp.Equal(s1, s2, cmpopts.EquateEmpty()))
fmt.Println("m1 equals m2 with option?", cmp.Equal(m1, m2, cmpopts.EquateEmpty()))
}
5.无序切片的元素
func main() {
//默认情况下,两个切片只有当长度相同,且对应位置上的元素都相等时,go-cmp才认为它们相等。
//如果,我们想要实现无序切片的比较(即只要两个切片包含相同的值就认为它们相等),
//可以使用cmpopts.SortedSlice选项先对切片进行排序,然后再进行比较:
s1 := []int{1, 2, 3, 4}
s2 := []int{4, 3, 2, 1}
s3 := make([]int, 5, 10)
s3 = s1[:]
fmt.Println("s1 equals s2?", cmp.Equal(s1, s2)) //false
fmt.Println("s1 equals s2 with option?", cmp.Equal(s1, s2, cmpopts.SortSlices(func(i, j int) bool { return i < j }))) //true
fmt.Println("s2 equals s3 with option?", cmp.Equal(s2, s3, cmpopts.SortSlices(func(i, j int) bool { return i < j }))) //true
//可以发现s2 和 s3 的长度容量显然不一致,但是 cmp.Equal(cmpopts.SortSlices) 还是判了相等,所以cmp.Equal(cmpopts.SortSlices)只管内容排序后实际的元素,至于长度,容量不管
m1 := map[int]int{1: 10, 3: 30, 2: 20}
m2 := map[int]int{1: 10, 2: 20, 3: 30}
fmt.Println("m1 equals m2?", cmp.Equal(m1, m2)) //true【可以发现map本身就不存在顺序可言,所以不用排序就可以认为相等】
fmt.Println("m1 equals m2 with option?", cmp.Equal(m1, m2, cmpopts.SortMaps(func(i, j int) bool { return i < j }))) //true
}