LoginSignup
18
7

More than 3 years have passed since last update.

Goでnilでもメソッドを実行できる

Last updated at Posted at 2019-12-31

こんにちは、ゴリラです。
せっかくの大晦日なので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()
}

これも推測ですが、
doFoo型の変数を受け取るので、*Fooから値を取ろうとしてnil pointerで落ちるのではないかと思います。

最後に

Go、奥深い…
頑張って公式ドキュメントを読もうと思いした。

ではみなさん、良いお年を〜

18
7
2

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