はじめに
さっき、次のような記事を書いたんですが、そもそも比較可能性について、あまり知られてなさそうということでまとめることにしました。
比較可能性(comparable)
Goには比較可能な型と比較できない型があります。たとえば、int
型やstring
型は比較できますが、スライスや関数は比較ができません。
言語仕様には、真偽値、整数値、浮動小数点値、複素数値、文字列、ポインタ、チャネル、インタフェース、構造体(条件あり)、配列(条件あり)が比較可能としています。
比較可能の場合、==
演算子および!=
演算子で比較できます。順序付け可能な場合は、さらに<
、<=
、>
、>=
演算子が使用できます。int
型などの数値型や文字列型がこれにあたります。
比較可能な型は、マップのキーに仕様できます。意外かもしれませんが、構造体や配列も次のようにマップのキーとして使用できます(一部を除く)。
type Point struct {
X int
Y int
}
m1 := map[Point]int{{X: 100, Y: 200}: 20}
fmt.Println(m1)
m2 := map[[2]int]int{{100, 200}: 30}
fmt.Println(m2)
入れ子になったコンポジットリテラルでは要素の型名を省略できるため便利ですね。キーが2つあるような場合に使えます。
比較できない型
比較できない型は、スライス、マップ、関数(メソッド値やメソッド式も含む)です。これらの型は、nil
との比較しかできません。
var s []int
var m map[any]any
var f func()
fmt.Println(s == nil, m == nil, f == nil) // OK
fmt.Println(s == s, m == m, f == f) // NG
なお、この他にも後述する一部の構造体と配列も比較できないものがあります。
構造体と配列の比較
構造体と配列は基本的には比較可能な型です。しかし、それらを構成するフィールドや要素に比較できない型がある場合は、比較できません。
たとえば、次のように関数の配列や構造体のフィールドにスライスがある場合は比較できません。
var x [2]func()
var y struct{ s []int }
fmt.Println(x == x, y == y) // NG
インタフェースの比較
インタフェースは比較可能です。しかし、インタフェースは動的型(dynamic type)と動的値(dynamic value)の2つから構成されるため、少し複雑です。
インタフェース値が等しいのは、動的型と動的値がともに等しい場合です。
動的型と動的値とは、たとえば、次のようなany
型の変数x
があった場合、int
型と100
です。
var x any
var n int = 100
x = n
// 動的型:int、動的値:200
fmt.Printf("動的型:%[1]T 動的値:%[1]v\n", x)
動的型はfmt.Printf
関数の%T
verbsで表示できます。ちなみに、[1]
は1番目の引数を表します。同じ引数について違う書式を使用して表示したい場合に用いると便利です。
次のようなany
型の変数x
と変数y
を比較するとfalse
になります。動的型はint
型で同じですが、動的値が違うためです。
var x, y any = 100, 200
fmt.Println(x == y)
次のように動的型が違う場合もfalse
になります。
var x, y any = int(100), int32(100)
fmt.Println(x == y)
動的型が同じであっても、比較できない型の場合はパニックが発生します。
var x, y any = []int{100}, []int{100}
fmt.Println(x == y)
もちろん、動的値が同じ(ように感じる場合)であってもパニックが発生します。
ns := []int{100}
var x, y any = ns, ns
fmt.Println(x == y)
比較する2つのインタフェース値の動的型のうち片方または両方が比較できない型であっても、同じ型でなければパニックにはならず、false
となります。
var x, y any = []int{100}, []int32{100}
fmt.Println(x == y)
表にまとめると次のようになります。
xの動的型がcomparable | yの動的型がcomparable | 動的型が同じ | 動的値が同じ | x == y |
---|---|---|---|---|
◯ | ◯ | ◯ | ◯ | true |
◯ | ◯ | ◯ | ☓ | false |
◯ | ◯ | ☓ | ◯ | false |
◯ | ◯ | ☓ | ☓ | false |
◯ | ☓ | ◯ | ◯ | panic |
◯ | ☓ | ◯ | ☓ | panic |
◯ | ☓ | ☓ | ◯ | false |
◯ | ☓ | ☓ | ☓ | false |
☓ | ◯ | ◯ | ◯ | panic |
☓ | ◯ | ◯ | ☓ | panic |
☓ | ◯ | ☓ | ◯ | false |
☓ | ◯ | ☓ | ☓ | false |
☓ | ☓ | ◯ | ◯ | panic |
☓ | ☓ | ◯ | ☓ | panic |
☓ | ☓ | ☓ | ◯ | false |
☓ | ☓ | ☓ | ☓ | false |
ざっくり、まとめると以下のようになります。
- 動的型が違う場合は必ず
false
- 動的型が同じでどちらかの動的型が比較不可能な場合はパニック
- 動的型が同じでcomparableでかつ動的値が同じ場合は
true
- 動的型が同じでcomparableでかつ動的値が違う場合は
false
おわりに
比較と一言でいってもなかなかの奥深さですね。