こんにちは、ゴリラです。
せっかくの大晦日なのでGoのメソッドの仕様についてわかった面白い話をしようと思います。
みかんでも食べながら読んでみてください。
nilでもメソッドを実行できる
このコードの実行結果はなんでしょうか?
package main
import (
"fmt"
)
type Foo int
func (f *Foo) do() {
fmt.Println(f)
}
func main() {
var f *Foo
f.do()
}
nil pointerになると思った方はいると思いますが(自分はそうでした)、
答えは<nil>
です。
なぜ<nil>
なのか
Goではメソッドはインスタンスを第1引数に受け取る関数になるとのことです。
公式に以下の様に書かれています。
The expression
`T.Mv`
yields a function equivalent to Mv but with an explicit receiver as its first argument; it has signature
`func(tv T, a int) int`
つまりfunc (f *Foo) do()
はfunc do(f *Foo)
として実行されます。
そのため、do()
にnilのfが渡されて<nil>
と出力されます。
インスタンスなくてもメソッドを実行できる
以下のコードの実行結果は何でしょうか?
package main
import (
"fmt"
)
type Foo int
func (f Foo) do() {
fmt.Println(f)
}
func main() {
Foo.do(1)
}
答えは1
です。
なぜ1なのか
まず、Foo
から直接do()
を呼び出せる理由ですが、Goではメソッドはインスタンスではなく型に紐付いているからです。
そして先程解説したとおり、func (f Foo) do()
はfunc do(f Foo)
になるので、Foo.do(1)
は1と出力されます。
ちなみに、1は暗黙的にFoo
に変換されるので正しく出力されます。
余談1
以下のコードの場合、引数は不要です。
package main
import (
"fmt"
)
type Foo int
func (f Foo) do() {
fmt.Println(f)
}
func main() {
var f Foo // fはFooのインスタンス
f.do()
}
これは推測ですが、
型で直接メソッドを実行する場合はインスタンス情報がないため引数で渡す必要だけど、
変数の場合はインスタンスがあるから引数は不要なのではないかと思います。
余談2
nilの*Foo
型変数から値レシーバーのメソッドを呼び出すとnil pointerになります。
package main
import (
"fmt"
)
type Foo int
func (f Foo) do() {
fmt.Println(f)
}
func main() {
var f *Foo // fはnil
f.do()
}
これも推測ですが、
do
はFoo
型の変数を受け取るので、*Foo
から値を取ろうとしてnil pointerで落ちるのではないかと思います。
最後に
Go、奥深い…
頑張って公式ドキュメントを読もうと思いした。
ではみなさん、良いお年を〜