# 4.4.4-12 类型的等价规则

Go 语言采用的是[结构化类型系统](https://en.wikipedia.org/wiki/Structural_type_system), 也就是说判断两个类型是否相等考虑的是类型的内在结构，而与类型的名字或具体的声明位置无关。判断两个类型是否等价的入口函数定义在`$GCROOT/compile/internal/type2/api.go`中：

```go
func Identical(x, y Type) bool { /* ... */ }
```

详细逻辑在`$GCROOT/compile/internal/types2/predicates.go`中：

```go
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类型等价需要符合如下条件：&#x20;

1. 属性数目相同，并且拥有相同的序列&#x20;
2. 对于同一个位置的两个属性，其名称相同，类型相同，并且 tag 相同&#x20;
3. 如果两个 struct 在不同的包中定义，则不能包含私有属性

我们看如下代码：

```go
// 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{})
}
```

通过上述代码可以发现，只要类型的结构是一致的，那么就是等价的。

{% hint style="info" %}
除了结构化类型系统之外，在编程语言中常用的类型系统还包括[Nominal Type System](https://en.wikipedia.org/wiki/Nominal_type_system)以及[Duck Typing](https://en.wikipedia.org/wiki/Duck_typing), 前者根据类型声明的位置以及名字来区分（Java），而后者根据类型的行为来区分（Python）。

随着对泛型支持，类型等价性比较会变得更加复杂，这在后面[方法的等价性校验](/golang-bian-yi-qi-lei-xing-jian-cha/4.5.32b-fang-fa-yu-shu-xing-cha-zhao.md#fang-fa-de-deng-jia-xing-xiao-yan)有更详细的介绍，现在该功能还在开发之中，可能后期完成之后会合并到一起。
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gocompiler.shizhz.me/golang-bian-yi-qi-lei-xing-jian-cha/4.4.412-lei-xing-de-deng-jia-gui-ze-fl.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
