golang の nil は型を持っている。type Hoge の nil と type Foo の nil は異なる。そのため nil 安全にチェックしまくっててもハマる時はハマる。。。[golang nil ハマる] とかでググると死ぬほど記事がある。
現象を再現するのはこんな感じ。
package main
import (
"fmt"
)
type HogeInterface interface{
Name() string
}
func exec(hoge HogeInterface) {
if hoge == nil {
fmt.Println("nil")
return
}
fmt.Println("name: " + hoge.Name())
}
type Hoge struct {
n string
}
func (h *Hoge) Name() string {
return h.n
}
func main(){
var hoge *Hoge
exec(hoge) // panic: runtime error: invalid memory address or nil pointer dereference
exec(&Hoge{n: "エビマヨ"})
}
このパターンだと HogeInterface
を実装するのは Hoge
であることは自明なので型変換してからチェックしてあげればいい。
- if hoge == nil {
+ if hoge.(*Hoge) == nil {
ただし, これだと「インターフェースの実装に依存」することになるのでよろしくない。 HogeInterface
を実装するのがもしかしたら SuperHoge
になるかもしれんし。。。
メタプロっぽく nil かを調べるなら, 以下できる。
- if hoge.(*Hoge) == nil {
+ if reflect.ValueOf(hoge).IsNil() {
これだけだと, interface{} 型の nil が来た場合, panic となってしまう。
+ exec(nil) // panic: reflect: call of reflect.Value.IsNil on zero Value
interface{} 型の nil チェックは単純に == で判定できる。というわけで正確には以下の判定が必要。
- if reflect.ValueOf(hoge).IsNil() {
+ if hoge == nil || reflect.ValueOf(hoge).IsNil() {
まんま以下をパクっただけである。感謝。