21
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

tenntennAdvent Calendar 2022

Day 7

Goの比較可能性(comparable)

Last updated at Posted at 2022-12-08

はじめに

どうもナレッジワークtenntennです。

さっき、次のような記事を書いたんですが、そもそも比較可能性について、あまり知られてなさそうということでまとめることにしました。

比較可能性(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)

The Go Playgroundで動かす

入れ子になったコンポジットリテラルでは要素の型名を省略できるため便利ですね。キーが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

The Go Playgroundで動かす

なお、この他にも後述する一部の構造体と配列も比較できないものがあります。

構造体と配列の比較

構造体と配列は基本的には比較可能な型です。しかし、それらを構成するフィールドや要素に比較できない型がある場合は、比較できません。

たとえば、次のように関数の配列や構造体のフィールドにスライスがある場合は比較できません。

var x [2]func()
var y struct{ s []int }
fmt.Println(x == x, y == y) // NG

The Go Playgroundで動かす

インタフェースの比較

インタフェースは比較可能です。しかし、インタフェースは動的型(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)

The Go Playgroundで動かす

動的型はfmt.Printf関数の%Tverbsで表示できます。ちなみに、[1]は1番目の引数を表します。同じ引数について違う書式を使用して表示したい場合に用いると便利です。

次のようなany型の変数xと変数yを比較するとfalseになります。動的型はint型で同じですが、動的値が違うためです。

var x, y any = 100, 200
fmt.Println(x == y)

The Go Playgroundで動かす

次のように動的型が違う場合もfalseになります。

var x, y any = int(100), int32(100)
fmt.Println(x == y)

The Go Playgroundで動かす

動的型が同じであっても、比較できない型の場合はパニックが発生します。

var x, y any = []int{100}, []int{100}
fmt.Println(x == y)

The Go Playgroundで動かす

もちろん、動的値が同じ(ように感じる場合)であってもパニックが発生します。

ns := []int{100}
var x, y any = ns, ns
fmt.Println(x == y)

The Go Playgroundで動かす

比較する2つのインタフェース値の動的型のうち片方または両方が比較できない型であっても、同じ型でなければパニックにはならず、falseとなります。

var x, y any = []int{100}, []int32{100}
fmt.Println(x == y)

The Go Playgroundで動かす

表にまとめると次のようになります。

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

おわりに

比較と一言でいってもなかなかの奥深さですね。

21
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?