まとめ
- 内部的にGoのインターフェースは(型, 値)という2つのペアを持つ。
- インターフェース型がnilになるのは(nil型, 0x0)という(型, 値)ペアのときだけ。
GoのFAQにて言及されています。
nilとerror型のnilとがイコールにならないのは?
内部的にインタフェースは「型」と「値」の2要素から成ります。この「値」は実際の任意な値であり、インタフェースの「動的な値」と呼ばれます。「型」はその値の型です。仮に、あるインタフェース値がint型の3であるとき(int, 3)と表すとします。
インタフェースの値がnilとなるのは、内部の値と型がともにセットされていないとき(nil, nil)だけです。詳しく述べれば、nilインタフェースは常にnil型を保持しています。ポインタ型であるintをインタフェース内に格納したとき、ポインタの値に関わらず内部の型はint(*int, ?)になります。ポインタの値がnilであっても、このインタフェース値はnilにはなりません。
検証コード
package main
func main() {
var x *int32 = nil
var y *int64 = nil
// xとnilの比較は値同士なので一致
println(x == nil) // true
// xとyはそれぞれ(*int32, nil), (*int64, nil)
// のペアのインターフェース型へキャストされる。
xx := interface{}(x)
yy := interface{}(y)
println(interface{}(x)) // (0x94b60,0x0)
println(interface{}(y)) // (0x94ba0,0x0)
// ペアの中身が違うのでxxとyyの比較は不一致
println(xx == yy) // false
// xx,yyは(nil, nil)ペアではないのでnilとの比較は不一致
println(xx == nil) // false
// 【ちなみに】 nilはnil値を持てる型へキャストできる
// nilはnil値の型*int32へキャスト可
println((*int32)(nil)) // 0x0 (<- nil)
// 型int32はnil値を持てないのでキャスト不可
// e := (int32)(nil) // コンパイルエラー
}
出力
true
(0x94b60,0x0)
(0x94ba0,0x0)
false
false
0x0