Goクイズ Advent Calendar 2020/12/2

この記事は [Goクイズアドベントカレンダー2020](https://qiita.com/advent-calendar/2020/goquiz)の記事です。

## 問題

次のコードを実行するとどうなるでしょうか？

```go
package main

import "fmt"

type S []int

func main() {
	var a, b interface{}
	a, b = []int{}, S{}
	fmt.Print(a == b)
}
```

- https://play.golang.org/p/s-UCNArIT9r

## 選択肢

1. `true`と表示される
2. `false`と表示される
3. `build error`
4. `panic`

<details>
<summary>解答と解説</summary>
<div>

正解: 2. `false`

本問に関係する主なGo言語仕様: 比較演算子 https://golang.org/ref/spec#Comparison_operators

## TL;DR

- `function`型、 `slice`型、および `map` 型の値は比較可能ではなく、比較演算子`==`および`!=`のオペランドになることができない。
  - ただし例外として、事前宣言された識別子としての`nil`とは比較することができる。
- interface型は比較可能で、`a == b`は次のようにふるまう。
  - `a`と`b`の動的な型と動的な値とが両方等しいときにのみtrueとなる。
  - `a`と`b`の動的型が等しくないときは、falseとなる。
  - `a`と`b`の動的型が等しく、かつその型の値が比較可能でないときには、run-time panicを引き起こす。

## 細かい解説

出題ポイントと直接関係ない部分を含めて、仕様書に基づいて順番に読んでいきます。適宜仕様書の該当箇所へのリンクを貼っているので、興味に応じて参照していただければと思います。

まず最初の行です。

```go
var a, b interface{}
```

これは[変数宣言](https://golang.org/ref/spec#Variable_declarations)で、`interface{}`という型を持つ変数`a, b`を宣言しています。`interface{}`は「何でも代入できる型」としてお馴染みだと思いますが、これをあえて仕様に基づいて説明すると、[メソッドセット](https://golang.org/ref/spec#Method_sets)が空集合であるような[interface型](https://golang.org/ref/spec#Interface_types)を表す[型リテラル](https://golang.org/ref/spec#TypeLit)です。

次の行に進みます。

```go
a, b = []int{}, S{}
```

これは[代入文](https://golang.org/ref/spec#Assignments)で、変数`a, b`に`[]int`および`S`の[`Composite literal`](https://golang.org/ref/spec#Composite_literals)を代入しています。この代入文が合法的であることは、次の[代入可能性](https://golang.org/ref/spec#Assignability)の仕様からわかります:

> T is an interface type and x implements T.

**Tがinterface型であり、値xがTを実装するとき（には、xをT型の変数に代入可能である)**

「実装する(implement)」の定義は[こちら](https://golang.org/ref/spec#Interface_types)にあります:

> A variable of interface type can store a value of any type with a method set that is any superset of the interface.  Such a type is said to implement the interface.

**interface型の変数は、そのmethod setを含むmethod setを持つ型の値を保持することができます。そのような型は、そのinterfaceを実装する、と言い表します。**

`interface{}`型は、「空の」method setを表すinterface型です。どんな型のmethod setであれ、「空の」method setを含みますから、どんな型も`interface{}`型を実装すると言えます。したがって、どんな型の値も、`interface{}`型の変数に代入可能です。これでこの行も問題ないことがわかりました。

問題は次の行です。

```go
fmt.Print(a==b)
```

[`fmt.Print`](https://golang.org/pkg/fmt/#Print)は、引数を(デフォルトのフォーマットで)標準出力に出力します。問題は`a==b`という式です。これについては[比較演算子](https://golang.org/ref/spec#Comparison_operators)のセクションに説明されています。少し長くなりますが、問題に関係する部分を抜き出して訳してみます。

> Comparison operators
> Comparison operators compare two operands and yield an untyped boolean value.

**比較演算子は、2つのオペランドを比較して、型なしboolean値を返します。**

> In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.

**どのような比較においても、1つめのオペランドは2つめのオペランドの型に代入可能でなければならず、2つめのオペランドも1つめのオペランドの型に代入可能でなければなりません。**

この問題では`a`と`b`どちらの型も`interface{}`型なので、お互いに代入可能になっており、問題ありません。

> The equality operators == and != apply to operands that are comparable. The ordering operators <, <=, >, and >= apply to operands that are ordered. These terms and the result of the comparisons are defined as follows:

**等値演算子`==, !=`は、比較可能であるようなオペランドに適用されます。順序演算子 `<, <=, >, >=`は順序付けされたオペランドに適用されます。これらの用語と各種の比較演算の結果は次のように定義されます:**

> Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.

**interfaceの値は比較可能です。2つのinterfaceの値が等しいのは、それらが同一の動的型を持ち、かつ、等しい動的な値をもつときか、あるいは両方が`nil`であるときです。**

> A comparison of two interface values with identical dynamic types causes a run-time panic if values of that type are not comparable. 

**2つのinterfaceの値であって同一の動的型をもつものを比較するとき、もしその型の値が比較可能でなければ、run-time panicを引き起こします。**

> Slice, map, and function values are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier nil.

**Slice, map, functionの値は比較可能ではありません。ただし、例外として、slice、map、functionの値は、事前宣言された識別子のnilと比較することができます。**

これを踏まえて、問題に戻ります。

```go
type S []int
// ...省略
var a, b interface{}
a, b = []int{}, S{}
fmt.Print(a == b)
```

というようになっていましたから、`a`の動的型は`[]int`, `b`の動的型は`S`です。そして`S`の基底型は`[]int`ですので、これらの値はいずれも比較可能ではありません。それではpanicするのでしょうか？

そうではありません。[defined type](https://golang.org/ref/spec#Type_definitions)である`S`は、他のいかなる型とも異なる型であり(※1)、`[]int`と`S`とはあくまでも異なる型なのです。`a==b`がpanicを引き起こすのは、あくまでも動的型が「同一」である場合に限ります。よって、この問題のように動的型が同一でない場合には、その型の値が比較可能かどうかを判断するまでもなく、`a`と`b`は等しくないと判断できて、`a==b`は`false`となります(※2)。

以上から、正解は2. の`false`となります。

(※1): https://golang.org/ref/spec#Type_identity を見ると、"A defined type is always different from any other type." という記述があります。

(※2): 動的型が同一でないときにnot equalであるということは実は明示的には書かれていないのですが、これは未定義というわけではなくて、反対解釈的にnot equalと定義されている、というのが筆者の解釈です。

</div>
</details>