7
1

More than 3 years have passed since last update.

Goクイズ Advent Calendar 2020/12/2

Last updated at Posted at 2020-12-01

この記事は Goクイズアドベントカレンダー2020の記事です。

問題

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

package main

import "fmt"

type S []int

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

選択肢

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


解答と解説

正解: 2. false

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

TL;DR

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

細かい解説

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

まず最初の行です。

var a, b interface{}

これは変数宣言で、interface{}という型を持つ変数a, bを宣言しています。interface{}は「何でも代入できる型」としてお馴染みだと思いますが、これをあえて仕様に基づいて説明すると、メソッドセットが空集合であるようなinterface型を表す型リテラルです。

次の行に進みます。

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

これは代入文で、変数a, b[]intおよびSComposite literalを代入しています。この代入文が合法的であることは、次の代入可能性の仕様からわかります:

T is an interface type and x implements T.

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

「実装する(implement)」の定義はこちらにあります:

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{}型の変数に代入可能です。これでこの行も問題ないことがわかりました。

問題は次の行です。

fmt.Print(a==b)

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

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つめのオペランドの型に代入可能でなければなりません。

この問題ではabどちらの型も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と比較することができます。

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

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

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

そうではありません。defined typeであるSは、他のいかなる型とも異なる型であり(※1)、[]intSとはあくまでも異なる型なのです。a==bがpanicを引き起こすのは、あくまでも動的型が「同一」である場合に限ります。よって、この問題のように動的型が同一でない場合には、その型の値が比較可能かどうかを判断するまでもなく、abは等しくないと判断できて、a==bfalseとなります(※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と定義されている、というのが筆者の解釈です。


7
1
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
7
1