如果创建类型为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()方法,它都会惊慌失措.

bytes.Buffer个.指向它的指针实现了io.Reader.但在nil *bytes.Buffer指针值上调用Buffer.Read()会引起panic .但这并不是因为你不能在nil个指针接收器上调用方法,而是因为bytes.Buffer.Read()的实现试图go 引用指针接收器,而这种go 引用操作正是导致panic 的原因:

// 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上try ):

aaaaaaaaa <nil>

所以结论是:usually种类型的方法带有指针接收器,它们需要一个非nil指针,因为它们使用pointed对象(在bytes.Buffer种情况下,它们使用指向 struct 的字段).为了使用这些类型(为了实现有意义的接口实现),您通常需要一个非nil指针值来让方法"工作".然而,正如上述myBuffer实现所示,这并不总是一项要求.你的工作是经常阅读所用类型和方法的文档,以避免此类误用(例如,试图使用nil*bytes.Buffer).

见相关问题:

Hiding nil values, understanding why Go fails here

Go reflection with interface embedded in struct - how to detect "real" functions?

Go相关问答推荐

gorm如何声明未自动更新的unix时间戳米尔斯字段

VS代码,Golang格式顽固的情况与switch / case

获取k8s群集作用域运算符的命名空间

如何使用GRPC UnaryClientInterceptor中的`maily`参数?

如何存储来自异步Goroutine的返回值列表?

困扰围棋官方巡回赛的S建议所有方法都使用同一类型的接收器

一次打印用户输入的字符串n次

Json.Unmarshal() 和 gin.BindJson() 之间的区别

当我使用 CircleCI 构建 Go Image 时,我得到runtime/cgo: pthread_create failed: Operation not allowed

启动套接字服务器会干扰 gRPC/http 客户端服务器通信 Golang

Yocto 无法交叉编译 GoLang Wails 应用程序

将 big.Int 转换为 [2]int64,反之亦然和二进制补码

函数超时和 goroutine 泄漏

将未知长度切片的值分配给Go中的 struct ?

如何使用golang操作很长的字符串以避免内存不足

Golang 泛型

如何在 Prometheus 中正确检测区域和环境信息?

如何动态解析 Go Fiber 中的请求正文?

Golang 与 Cassandra db 使用 docker-compose:cannot connect (gocql)

空接口与泛型接口有何不同?