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.