如何正确检查 io.Reader 是否为 nil?

How to correctly check io.Reader for nil?

如果你创建了一个bytes.Buffer类型的变量(没有初始化)并将它赋值给一个io.Reader类型的字段,那么在检查io.Reader是否为nil之后会出现错误:invalid memory address or nil pointer dereference。如何正确检查以避免此类错误?

Playground

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
    }
}

要检查 io.Reader(或任何其他接口)值是否为 nil,您只需将其与 nil.

进行比较

nil io.Reader是否是一个有意义的实现,这是另一个问题。

例如这个实现有意义吗?

type panicReader struct{}

func (panicReader) Read(p []byte) (int, error) {
    panic("foo")
}

panicReader当然实现了io.Reader,但是每当你调用它的Read()方法时,它总是会panic。

nil *bytes.Buffer 指针值上有 bytes.Buffer. A pointer to it implements io.Reader. But calling Buffer.Read() 会出现 panic。但不是因为你不能在 nil 指针接收器上调用方法,而是因为 bytes.Buffer.Read() 的实现试图取消引用指针接收器,而这种取消引用操作是导致恐慌的原因:

// Excerpt from bytes.Buffer.Read implementation 
func (b *Buffer) Read(p []byte) (n int, err error) {
    b.lastRead = opInvalid
    if b.empty() {
    // ...
}

你不能在这里做出一般性的结论(暂时)。请参阅此 io.Reader 实施:

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实现io.Reader,其Read()方法不使用指针接收值。这是什么意思?您 可以 nil *myBuffer 值上调用 Read()

var data *myBuffer

request := &Request{
    Body: data,
}

if request.Body != nil {
    data, err := ioutil.ReadAll(request.Body)
    fmt.Println(string(data), err)
}

这将输出(在 Go Playground 上尝试):

aaaaaaaaa <nil>

所以结论是这样的:通常 具有指针接收器方法的类型需要非nil 指针,因为它们使用 pointed 对象(在 bytes.Buffer 的情况下,他们使用指向结构的字段)。要使用此类类型(对已实现的接口进行有意义的实现),您通常需要一个非 nil 指针值才能使方法“工作”。然而,正如上面的 myBuffer 实现所示,这并不总是一个要求。您的工作是始终阅读所用类型和方法的文档以避免此类误用(例如尝试使用 nil *bytes.Buffer)。

参见相关问题: