Golang函数或方法传递nil值的一个坑

发布时间 2023-09-22 10:55:08作者: caibaotimes
package t13_niu_error

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "net/http"
    "testing"
)

// 将请求获取的数据转为string,支持GET或POST请求
func BaseRequestString(requestMethod, url string, requestBody *bytes.Reader) (string, error) {
    client := &http.Client{
    }
    var req *http.Request
    var err error
    // 特别注意这里,即使外边requestBody传nil的话
    // 但是它的类型type是 *bytes.Reader,传给http.NewRequest方法的最后一个参数不是需要的io.Reader类型的!会报错
    req, err = http.NewRequest(requestMethod, url, requestBody)

    if err != nil {
        return "", err
    }
    res, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer res.Body.Close()
    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        return "", err
    }
    return string(body), nil

}

// 测试
func TestNilError(t *testing.T) {
    // GET
    if res, err := BaseRequestString("GET", "https://www.baidu.com", nil); err != nil {
        fmt.Println("err>> ", err.Error())
    } else {
        fmt.Println("res>> ", res)
    }
}

如果按照这种实现的方式,程序会上报一个错误:

panic: runtime error: invalid memory address or nil pointer dereference [recovered]

其实错误就出在了nil的类型不一样。

我们可以看到,在函数定义阶段,requestBody定义的是bytes.Reader类型的,也就是说外部传入的nil也是bytes.Reader类型的。

但是我们再在http.NewRequest方法中看一下它的源码:

// NewRequest wraps NewRequestWithContext using the background context.
func NewRequest(method, url string, body io.Reader) (*Request, error) {
    return NewRequestWithContext(context.Background(), method, url, body)
}

在Go语言中,变量总是被一个定义明确的值初始化,即使接口类型也不例外。对于一个接口的零值就是它的类型和值的部分都是nil:

这就很明白了:两个nil值的value虽然都是nil,但是它们的type不同!所以将bytes.Reader类型的nil传入需要io.Reader类型的nil参数的位置,肯定会报错的!
解决:
requestBody改为接口类型 requestBody interface{}就可以了