Go言語では、nil
の構造体ポインタにアクセスする際には注意が必要です。
特に、フィールドとメソッドにアクセスする場合の動作は異なるため、それぞれのケースについて詳しく解説します。
フィールドにアクセスする場合
nil
の構造体ポインタに対してフィールドにアクセスしようとすると、ランタイムパニックが発生します。
package main
import "fmt"
type Foo struct {
Name string
}
func main() {
var foo *Foo
fmt.Println(foo.Name) // panic: runtime error: invalid memory address or nil pointer dereference
}
このコードを実行すると foo
が nil
であるため、フィールド Name
にアクセスしようとする際にランタイムパニックが発生します。
このような場合、フィールドにアクセスする前にポインタが nil
でないことを確認する必要があります。
package main
import "fmt"
type Foo struct {
Name string
}
func main() {
var foo *Foo
if foo != nil {
fmt.Println(foo.Name)
} else {
fmt.Println("foo is nil") // foo is nil
}
}
メソッドにアクセスする場合
Go言語では、ポインタレシーバを使用したメソッドは nilポインタでも呼び出すことができます。
これは、メソッド呼び出しがレシーバを最初の引数とする関数呼び出しに変換されるためです。
package main
import "fmt"
type Foo struct{}
func (f *Foo) Bar() string {
return "bar"
}
func main() {
var foo *Foo
fmt.Println(foo.Bar()) // bar
}
このコードでは、foo
が nil
であっても、メソッド Bar
を正常に呼び出すことができます。
エラー処理とnilチェック
レシーバが nil
のときに発生する panic は、通常、プログラミングのミスを示すものです。
メソッドの呼び出し前に nil
チェックを行い、事前に適切な処理を行う方法が一般的です。
package main
import "fmt"
type Foo struct{}
func (f *Foo) Bar() string {
return "bar"
}
func main() {
var foo *Foo
if foo != nil {
fmt.Println(foo.Bar())
} else {
fmt.Println("foo is nil") // foo is nil
}
}
ただし、場合によってはメソッド内でnilチェックを行い、処理することもあります。
例えば、レシーバがnilであることを特別なケースとして処理する必要がある場合です。
package main
import "fmt"
type Foo struct{}
func (f *Foo) Bar() string {
if f == nil {
return "foo is nil"
}
return "bar"
}
func main() {
var foo *Foo
fmt.Println(foo.Bar()) // foo is nil
}
まとめ
- フィールドにアクセスする場合: フィールドにアクセスする前にポインタが
nil
でないことを確認する必要があります - メソッドにアクセスする場合: ポインタレシーバを使用したメソッドは nilポインタでも呼び出すことができますが、メソッド内で nilチェックを行い、適切に処理することが重要です
これらのポイントを理解することで、Go言語における nil
の構造体ポインタの扱い方を正しく実装できるようになります。
参考
- 実用Go言語 - 3.3.2 レシーバーはnilでもメソッドは呼べる
- Go言語100Tips ありがちなミスを把握し、実装を最適化する - 6.4 No.45:nilレシーバを返す