# 4.4.4-13 类型的比较规则

除了类型的等价性，程序中还经常需要对比两个实例是否相等，或者其大小关系，这类操作通过比较运算符来实现。go 定义了 6 个[比较运算符](https://golang.org/ref/spec#Comparison_operators)（==, !=, >=, <=, >, <）, 其中 != 与 == 应用于可比较类型（Comparable type），而剩下四个运算符应用于可排序类型（Ordered type）；Ordered type 都是与数字、字符串相关的基础类型，在[基础类型的 BasicInfo](/golang-bian-yi-qi-lei-xing-jian-cha/4.4.43-lei-xing-shu-ju-jie-gou-ji-chu-lei-xing.md)中有明确定义。Comparable type 指的是那些可以判断不同实例是否相等的类型，例如 map 的 key, 就只能使用 Comparable Type 的类型。所有的基本类型都是 Comparable 的，除此之外还有一些复合类型也是 Comparable Type，各种类型的比较规则在[go 语言规范中](https://golang.org/ref/spec)有详细的说明。

在文件`$GCROOT/compile/internal/type2/predicates.go`中定义了各种判断类型属性的方法，其中就包括判断给定类型是否是可排序的，或者是否是可比较的，代码如下：

```go
func isOrdered(typ Type) bool { return is(typ, IsOrdered) }
func Comparable(T Type) bool  { return comparable(T, nil) } // 
func comparable(T Type, seen map[Type]bool) bool {
	if seen[T] {
		return true
	}
	if seen == nil {
		seen = make(map[Type]bool)
	}
	seen[T] = true

	// If T is a type parameter not constrained by any type
	// list (i.e., it's underlying type is the top type),
	// T is comparable if it has the == method. Otherwise,
	// the underlying type "wins". For instance
	//
	//     interface{ comparable; type []byte }
	//
	// is not comparable because []byte is not comparable.
	if t := asTypeParam(T); t != nil && optype(t) == theTop {
		return t.Bound().IsComparable()
	}

	switch t := optype(T).(type) {
	case *Basic:
		// assume invalid types to be comparable
		// to avoid follow-up errors
		return t.kind != UntypedNil
	case *Pointer, *Interface, *Chan:
		return true
	case *Struct:
		for _, f := range t.fields {
			if !comparable(f.typ, seen) {
				return false
			}
		}
		return true
	case *Array:
		return comparable(t.elem, seen)
	case *Sum:
		pred := func(t Type) bool {
			return comparable(t, seen)
		}
		return t.is(pred)
	case *TypeParam:
		return t.Bound().IsComparable()
	}
	return false
}
```

可以发现 Function 类型不在 comparable 函数中的任何一个 case 中，因此它是不可比较的，而 struct 类型可比较的前提是其所有属性都是可比较类型。来看如下代码：

```go
func typecheck() {
	type User struct {
		Name string
	}

	type People struct {
		Name string
	}

	var u1, u2 User

	// case 1: 相同类型，且类型可比较，无编译错误
	if u1 == u2 {
		// No compile error
	}

	// case 2: 不同类型，虽然两个类型都是可比较类型，但由于类型不同，相互之间无法比较
	var p People
	if u1 == p {
		// Compile error: cannot compare u1 == p (mismatched types User and People) [MismatchedTypes]
	}

	// case 3: 相同类型，但函数类型不可比较，所以变量之间不可比较
	var f1 func()
	var f2 func()

	if f1 == f2 {
		// Compile error: cannot compare f1 == f2 (operator == not defined for func())
	}
}
```

上例中 User 类型所有的属性都是可比较的，所以 u1 == u2 没有问题，而 u1 与 p 是不同的类型，所以无法比较。再看如下代码：

```go
type User struct {
	Name string
	Age  func() int
}

var u1, u2 User

if u1 == u2 {
	// Compile error: cannot compare u1 == u2 (operator == not defuned for User) [UndefinedOp]
}
```

当我们给 User 添加一个函数字段时，类型检查器报告了编译错误，因为通过上面 comparable 方法可以发现，函数属性的加入让 User 变成不可比较的类型了，所以类型检查器会说`==` 操作符对该类型没有定义。


---

# 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.413-lei-xing-de-bi-jiao-gui-ze.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.
