If you create a variable of type bytes.Buffer
(without initialization) and assign it to a field of type io.Reader
, then after checking io.Reader
for nil there will be an error: invalid memory address or nil pointer dereference
. How to check this correctly to avoid such errors?
package main
import (
"bytes"
"io"
"io/ioutil"
)
type Request struct {
Body io.Reader
}
func main() {
var data *bytes.Buffer
request := &Request{
Body: data,
}
if request.Body != nil {
ioutil.ReadAll(request.Body) // panic: runtime error: invalid memory address or nil pointer dereference
}
}
CodePudding user response:
To check if an io.Reader
(or any other interface) value is nil
, you simply compare it to nil
.
Whether a non-nil
io.Reader
is a meaningful implementation, that's another question.
E.g. is this implementation meaningful?
type panicReader struct{}
func (panicReader) Read(p []byte) (int, error) {
panic("foo")
}
panicReader
certainly implements io.Reader
, but whenever you call its Read()
method, it will always panic.
There is bytes.Buffer
. A pointer to it implements io.Reader
. But calling Buffer.Read()
on a nil
*bytes.Buffer
pointer value will panic. But not because you can't call methods on nil
pointer receivers, but because the implementation of bytes.Buffer.Read()
tries to dereference the pointer receiver, and this dereference operation is what causes the panic:
// Excerpt from bytes.Buffer.Read implementation
func (b *Buffer) Read(p []byte) (n int, err error) {
b.lastRead = opInvalid
if b.empty() {
// ...
}
You can't make a general conclusion here (just yet). See this io.Reader
implementation:
type myBuffer struct{}
var count int
func (*myBuffer) Read(p []byte) (int, error) {
if len(p) > 0 {
count
if count >= 10 {
return 0, io.EOF
}
p[0] = 'a'
return 1, nil
}
return 0, nil
}
*myBuffer
implements io.Reader
, and its Read()
method does not use the pointer receiver value. What does this mean? You can call Read()
on a nil
*myBuffer
value:
var data *myBuffer
request := &Request{
Body: data,
}
if request.Body != nil {
data, err := ioutil.ReadAll(request.Body)
fmt.Println(string(data), err)
}
This will output (try it on the Go Playground):
aaaaaaaaa <nil>
So the conclusion is this: usually types that have methods with pointer receiver require a non-nil
pointer because they use the pointed object (in case of bytes.Buffer
they use the fields of the pointed struct). To use such types (to have a meaningful implementation of implemented interfaces), you often need a non-nil
pointer value for the methods to "work". This–however–is not always a requirement as the above myBuffer
implementation shows. It's your job to always read the documentation of the used types and methods to avoid such misuses (e.g. trying to use a nil
*bytes.Buffer
).
See related question:
Hiding nil values, understanding why Go fails here
Go reflection with interface embedded in struct - how to detect "real" functions?