func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
// types must be expanded for comparison
x = expandf(x)
y = expandf(y)
if x == y {
return true
}
switch x := x.(type) {
case *Basic:
// Basic types are singletons except for the rune and byte
// aliases, thus we cannot solely rely on the x == y check
// above. See also comment in TypeName.IsAlias.
if y, ok := y.(*Basic); ok {
return x.kind == y.kind
}
case *Struct:
// Two struct types are identical if they have the same sequence of fields,
// and if corresponding fields have the same names, and identical types,
// and identical tags. Two embedded fields are considered to have the same
// name. Lower-case field names from different packages are always different.
if y, ok := y.(*Struct); ok {
if x.NumFields() == y.NumFields() {
for i, f := range x.fields {
g := y.fields[i] // 对应位置的属性
if f.embedded != g.embedded ||
cmpTags && x.Tag(i) != y.Tag(i) ||
!f.sameId(g.pkg, g.name) ||
!check.identical0(f.typ, g.typ, cmpTags, p) {
return false
}
}
return true
}
}
// 以下每个 case 内部的代码都省略了,详细实现参考源文件
case *Array:
case *Slice:
case *Pointer:
case *Tuple:
case *Signature:
case *Sum:
case *Interface:
case *Map:
case *Chan:
case *Named:
case *TypeParam:
case *bottom, *top:
case nil:
default:
unreachable()
}
return false
}
该函数内部的每个 case 对应一种类型,然后使用递归的方式检查类型的内部结构是否一致,我们留下Basic及Struct类型来做参考。两个Basic如果是同一种类型,则彼此等价;而两个Struct类型等价需要符合如下条件:
属性数目相同,并且拥有相同的序列
对于同一个位置的两个属性,其名称相同,类型相同,并且 tag 相同
如果两个 struct 在不同的包中定义,则不能包含私有属性
我们看如下代码:
// package user
type User struct { // User 定义在 package user 中
Name string
}
// package main
type People struct { // People 定义在 package main 中,其内部结构与 user.User 一致
Name string
}
func buildProfile(u struct { // 函数的参数类型是一个无名类型,其结构与 user.User、People 均相同
Name string
}) {
fmt.Printf("%v\n", u)
}
func main() {
// OK, 将 user.User 作为函数参数是合法的
buildProfile(user.User{})
// OK, 将 People 作为函数参数是合法的
buildProfile(People{})
// OK, 将无名类型字面量作为函数参数是合法的
buildProfile(struct {
Name string
}{
Name: "Jerry",
})
// OK, 可以直接将 user.User 类型转换为 People 类型
var _ user.User = user.User(People{})
}