nil指针嵌入错误

Why do I get a nil pointer error when I try to print a pointer with an uninitialized embedded error:

package main

import (
  "log"
  "errors"
)

type Danger struct {
  error
}

func main() {
  // the nil pointer issue has to do with struct embedding an error value that is nil
  d := &Danger{}
  log.Println(d)

  d = &Danger{errors.New("foobar")}
  log.Println(d)
}

results in

2009/11/10 23:00:00 %!v(PANIC=runtime error: invalid memory address or nil pointer dereference)
2009/11/10 23:00:00 foobar

https://play.golang.org/p/fBuN0XonX9v

This came up in an interview today and neither interviewer nor interviewee could figure it out.

The spec says:

Given a struct type S and a defined type T, promoted methods are included in the method set of the struct as follows:

  • If S contains an embedded field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.

The fmt documentation says:

If an operand implements the error interface, the Error method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).

From this, we can conclude that log.Println(d) will invoke the promoted Error method from the error field.

If the error field is nil, then the call panics.

The fmt documentation also says:

If an Error or String method triggers a panic when called by a print routine, the fmt package reformats the error message from the panic, decorating it with an indication that it came through the fmt package.

The text %!v(PANIC=runtime error: invalid memory address or nil pointer dereference) is the decorated panic value.