![1fe453cf30ea88dc88dd37941c406ee8.png](https://i-blog.csdnimg.cn/blog_migrate/1cef0667c5f609ccc607c4112e22d4b5.jpeg)
简介
我们时常有比较两个值是否相等的需求,最直接的方式就是使用==
操作符,其实==
的细节远比你想象的多,我在深入理解 Go 之==中有详细介绍,有兴趣去看看。但是直接用==
,一个最明显的弊端就是对于指针,只有两个指针指向同一个对象时,它们才相等,不能进行递归比较。为此,reflect
包提供了一个DeepEqual
,它可以进行递归比较。但是相对的,reflect.DeepEqual
不够灵活,无法提供选项实现我们想要的行为,例如允许浮点数误差。所以今天的主角go-cmp
登场了。go-cmp
是 Google 开源的比较库,它提供了丰富的选项。最初定位是用在测试中。
感谢thinkgos的推荐!
快速使用
先安装:
$ go get github.com/com/google/go-cmp/cmp
后使用:
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
)
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)
fmt.Println("u1 equals u2?", cmp.Equal(u1, u2))
c1 := &Contact{Phone: "123456789", Email: "dj@example.com"}
c2 := &Contact{Phone: "123456789", Email: "dj@example.com"}
u1.Contact = c1
u2.Contact = c1
fmt.Println("u1 == u2 with same pointer?", u1 == u2)
fmt.Println("u1 equals u2 with same pointer?", cmp.Equal(u1, u2))
u2.Contact = c2
fmt.Println("u1 == u2 with different pointer?", u1 == u2)
fmt.Println("u1 equals u2 with different pointer?", cmp.Equal(u1, u2))
}
上面的例子中,我们将==
与cmp.Equal
放在一起做个比较:
- 在指针类型的字段
Contact
未设置时,u1 == u2
和cmp.Equal(u1, u2)
都返回true
; - 两个结构的
Contact
字段都指向同一个对象时,u1 == u2
和cmp.Equal(u1, u2)
都返回true
; - 两个结构的
Contact
字段指向不同的对象时,尽管这两个对象包含相同的内容,u1 == u2
也返回了false
。而cmp.Equal(u1, u2)
可以比较指针指向的内容,从而返回true
。
以下是运行结果:
u1 == u2? true
u1 equals u2? true
u1 == u2 with same pointer? true
u1 equals u2 with same pointer? true
u1 == u2 with different pointer? false
u1 equals u2 with different pointer? true
高级选项
未导出字段
默认情况下,cmp.Equal()
函数不会比较未导出字段(即字段名首字母小写的字段)。遇到未导出字段,cmp.Equal()
直接panic
。这一点与reflect.DeepEqual()
有所不同,后者也会比较未导出的字段。
我们可以使用cmdopts.IgnoreUnexported
选项忽略未导出字段,也可以使用cmdopts.AllowUnexported
选项指定某些类型的未导出字段需要比较。
package main
import (
"fmt"
"github.com/google/go-cmp/cmp"
)
type Contact struct {
Phone string
Email string
}
type User struct {
Name string
Age int
contact *Contact
}
func main() {
c1 := &Contact{Phone: "123456789", Email: "dj@example.com"}
c2 := &Contact{Phone: "123456789", Email: "dj@example.com"}
u1 := User{"dj",