できない.dev

Go で「invalid memory address or nil pointer dereference」が解決できない

Go の `runtime error: invalid memory address or nil pointer dereference` は、nil のポインタやインタフェースに対してフィールドアクセス・メソッド呼び出しを行ったときに発生します。
代入直前に nil チェックを入れ、関数の戻り値で error を先に確認するのが基本対処です。

#nil#pointer#panic#runtime-error

公開:

要約

panic: runtime error: invalid memory address or nil pointer dereference は、Go のポインタが nil のままフィールドアクセスやメソッド呼び出しをしたときに発生します。
多くは関数の戻り値で error を確認せずに使っているか、interface に nil のポインタを入れて != nil 判定が誤動作しているのが原因です。
使用直前で nil チェックを入れ、戻り値の規約を統一すれば大半は防げます。

よくある原因

  1. 初期化していない var p *T をそのまま使っている
  2. func New() (*T, error) の戻り値で error を見ずに *T を呼び出している
  3. interface 値(例: var e error)に nil のポインタを代入しており、if e != niltrue になりながら中身は nil
  4. map[string]*Foo で未登録キーを引いて nil が返ったのに気付かず参照している

解決策

1. 使用直前で nil チェックする

func process(u *User) error {
    if u == nil {
        return fmt.Errorf("user is nil")
    }
    fmt.Println(u.Name)
    return nil
}

2. error を先に確認する

u, err := repo.FindByID(id)
if err != nil {
    return err
}
fmt.Println(u.Name)

戻り値の規約として「error が nil の時はポインタも非 nil」を徹底すると、毎回の nil チェックが不要になります。

3. nil interface の罠を避ける

type MyErr struct{}
 
func (*MyErr) Error() string { return "my error" }
 
func mayFail(cond bool) error {
    var e *MyErr
    if cond {
        e = &MyErr{}
    }
    return e // ← 型付き nil が interface にラップされ、!= nil が true になる
}

戻り値型が error の関数では失敗時のみ非 nil のポインタを返すように分岐を明示します(公式 FAQ)。

func mayFail(cond bool) error {
    if cond {
        return &MyErr{}
    }
    return nil // 明示的に untyped nil を返す
}

4. map アクセスは存在確認を付ける

if v, ok := users[id]; ok {
    fmt.Println(v.Name)
} else {
    return fmt.Errorf("user %s not found", id)
}

ポインタ値が入る map では、未登録キーで返るゼロ値 (nil) を二項代入で必ず捕捉してから参照します。

この記事は役立ちましたか?