突然ですが、以下のコードが何を意味しているかわかりますか?
var _ I = (*T)(nil)
答え
型チェックです。
次のようなコードが存在するとして、TがインターフェイスIを満たすことを宣言しています。
type I interface {}
type T struct {}
このチェックが行われるのは実行時ではなくコンパイル時です。また、割り当て先がブランク識別子_
であるため、バイトコードにはこのコードに対応する部分は出力されません。
Effective Go - The Go Programming Language
それはわかったけど、(*T)(nil)
ってキモくない? というか何してるの?
型の変換です。普通であればfloat32(1)
のように書けますが、前に*
がつく場合はカッコで囲む必要があります。*
はポインタを現すために使うので、その場合と区別するために必要だとか。
Go's Declaration Syntax - The Go Blog
つまり、次の2つのコードは同じことをしています。
var a = (*T)(nil)
var b *T
後者を使用して冒頭のコードを書き換えるなら次のようになります。
var b *T
var _ I = b
ここで、a
はnil
から生成され、b
はデフォルト値で初期化されていますので、両者共に直感的にはnil
だと思われるのですが、実はこれらは同時にインターフェイス値でもあります。
nilが型を持っている?
次のコードと実行結果を眺めてみてください。
package main
import (
"fmt"
)
type T struct {}
func main() {
var b *T
fmt.Println(b == nil)
}
true
b
はnil
なので当然の結果ですね。
次にreflect
を使って型を調べてみます。
package main
import (
"fmt"
"reflect"
)
type T struct {}
func main() {
var b *T
fmt.Println(reflect.TypeOf(b))
}
*main.T
どうやらb
は型と値を持っているようです。
こうなると、b
はただのnil
ではなくインターフェイス値であると考えるのが自然ですね。
インターフェイス値は動的な型と値を持つ
簡単な例として、空のインターフェイスI
を定義して、インターフェイス値i
にT
のインスタンスへのポインタを代入してみます。
package main
import (
"fmt"
"reflect"
)
type I interface {}
type T struct {}
func main() {
var i I
fmt.Println(i == nil)
fmt.Println(reflect.TypeOf(i))
i = &T{}
fmt.Println(i == nil)
fmt.Println(reflect.TypeOf(i))
i = nil
fmt.Println(i == nil)
fmt.Println(reflect.TypeOf(i))
}
true
<nil>
false
*main.T
true
<nil>
インターフェイス値i
の型と値は変化し、最後は再びnil
を代入され、元の状態に戻りました。
インターフェイス値は別の型に変換できる
代入ではなく、明示的な変換を行ってみます。
U
という構造体を追加しました。
package main
import (
"fmt"
"reflect"
)
type T struct {}
type U struct {}
func main() {
var b *T
var c = (*U)(b)
fmt.Println(c == nil)
fmt.Println(reflect.TypeOf(c))
}
true
*main.U
b
をU
に変換したものは依然としてnil
を値として持ちますが、型はU
になります。
ただし、interface{}
への変換を除く
では次にb
をinterface{}
に変換してみます。
package main
import (
"fmt"
)
type T struct {}
func main() {
var b *T
var d = interface{}(b)
fmt.Println(d == nil)
fmt.Println(reflect.TypeOf(d))
}
false
*main.T
おや…? もう少し検証してみましょう。再び構造体U
とb
をU
に変換したc
を追加します。
そしてb
とc
をそれぞれinterface{}
に変換してみます。
package main
import (
"fmt"
"reflect"
)
type T struct {}
type U struct {}
func main() {
var b *T
var c = (*U)(b)
var d = interface{}(b)
var e = interface{}(c)
fmt.Println(reflect.TypeOf(b))
fmt.Println(reflect.TypeOf(c))
fmt.Println(reflect.TypeOf(d))
fmt.Println(reflect.TypeOf(e))
}
*main.T
*main.U
*main.T
*main.U
T
そしてU
をinterface{}
に変換した場合、型が変化していないことに注目してください。
これが、先ほどnil
との比較がfalse
になった原因です。
まとめ
-
Goにはインターフェイス値というものがあり、型と値を持っている。
-
インターフェイス値の型と値はそれぞれ動的である。
-
nil
以外のものからinterface{}
に変換されたインターフェイス値は、値としてはnil
を持っていたとしても、依然としてnil
以外の型を持つため、nil
との比較がfalse
になる。
もしこの記事に興味を持っていただけたなら、Go Data Structures: Interfaces あるいは「プログラミング言語 Go 7章 インターフェイス」に「インターフェイス値」「インターフェイスの動的な型」「インターフェイスの動的な値」「型記述子」などの情報がまとまっています。