无法比较的Go结构体
比较运算符
在 Go 中比较运算符用于比较两个操作数的大小,并产生一个布尔值。
1 | |
其中 == 和!= 操作符号用于两个 comparable 的类型, 而 <, <=, >, and >= 用于比较 ordered 的类型。类型的定义如下:
| 类型 | comparable | ordered |
|---|---|---|
| Bool | ✅ | ❌ |
| Integer | ✅ | ✅ |
| Float-point | ✅ | ✅ |
| Complex | ✅ | ✅ |
| String | ✅ | ✅ |
| Pointer | ✅指向同一个值, 或者都是 nil | ❌ |
| Channel | ✅由相同的 make 创建或者都是 nil | ❌ |
| Interface(非泛型) | ✅ | ❌ |
| 接口的实现 | ✅ 非接口类型的 X 如果实现了 T,那么它实现了 T 的 t 是可比较的 | ❌ |
| Struct | ✅所有的字段都是可比较的,按照顺序比较 | ❌ |
| Array | ✅元素类型是可比较的 | ❌ |
| Type parameters | ✅当类型为 strictly comparable | ❌ |
无内存分配的不可比较 Struct
1 | |
编译上面的代码会得到下面的错误信息
1 | |
根据上结的规则, 结构体是否可以比较是根据字段是否可以比较的来判断的。在 Foo 中我们嵌入了一个匿名的 nocmp 类型的数据,nocmp 是一个长度为 0 的函数数组, 由于函数类型是无法比较的, 所以 nocmp 就无法比较,进一步使得 Foo 无法比较。
另外,这里使用了长度为 0 的函数数组,而不是直接定义一个函数类型的字段,如下面这段代码所示
1 | |
运行这段代码会得到结果
1 | |
可以看到,如果直接内嵌一个函数类型的值,在初始化结构体时,就会多分配一段内存用于保存函数类型的指针。
而使用 nocmp 则不会产生额外的内存分配。
因此,当我们需要定义一个不可比较的结构体时,可以通过内嵌一个 [0]func() 类型的匿名字段,在达到目的的同时避免的内存的浪费。
参考
无法比较的Go结构体
https://blog.zhangliangliang.cc/post/go-no-compare-struct.html