无法比较的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