为什么接口类型不提供“IsNil”方法?

sta*_*wer 0 null pointers interface go

首先让我们考虑以下几点:

func process(body io.Reader) {
    fmt.Printf("body == nil ? %+v\n", body == nil)
}

func main() {
    var body *bytes.Buffer
    fmt.Printf("body == nil ? %+v\n", body == nil)
    process(body)
    process(nil)
}
Run Code Online (Sandbox Code Playgroud)

这是输出:

body == nil ? true
body == nil ? false  // Did you get this right?
body == nil ? true
Run Code Online (Sandbox Code Playgroud)

另一个例子:

type Container struct {
    Reader io.Reader
}

func processContainer(container Container) {
    fmt.Printf("container.Reader == nil ? %+v\n", container.Reader == nil)
}

func main() {
    var body *bytes.Buffer
    processContainer(Container{Reader: body})
    processContainer(Container{Reader: nil})
}
Run Code Online (Sandbox Code Playgroud)

输出:

container.Reader == nil ? false // Did you get this right?
container.Reader == nil ? true
Run Code Online (Sandbox Code Playgroud)

对此的解释在https://golang.org/doc/faq#nil_error

一个简单的解决方案是让== nil测试只在接口对象包含nil值时返回 true 。但这会违反 的传递性==,因为它会断言两个具有 nil 值但不同接口类型的对象在==.

但是,我想知道是否应该IsNil()在所有接口类型上都有一个方法来解决这个问题?

另一个例子,来自 Go http 客户端的这一行,可以意外地抓住你:

https://github.com/golang/go/blob/master/src/net/http/client.go#L545

所以如果你这样称呼它

var body *bytes.Buffer
http.NewRequest(method, path, body)
Run Code Online (Sandbox Code Playgroud)

你会得到一个 nil 指针异常,尽管从源代码来看,这不应该发生。

编辑

抱歉,我引用了 Go http 源代码的错误行,现已更正。但这个例子仍然成立。

编辑 2

我已经强调了我的问题,以明确我在问什么。

icz*_*cza 5

首先阅读这个相关问题:隐藏 nil 值,理解为什么 golang 在这里失败

您可以nil通过将其与nil.

如果要检查包裹在非nil接口中的值是否为nil,可以使用反射:reflect.Value.IsNil()

请参阅此修改后的示例:

func process(body io.Reader) {
    fmt.Printf("process(): body == nil ? %t\n", body == nil)
    if body != nil {
        fmt.Println("\tINSIDE IsNil():", reflect.ValueOf(body).IsNil())
    }
}

func main() {
    var body *bytes.Buffer
    fmt.Printf("main(): body == nil ? %t\n", body == nil)
    process(body)
    process(nil)
    process(&bytes.Buffer{})
}
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上试试):

main(): body == nil ? true
process(): body == nil ? false
    INSIDE IsNil(): true
process(): body == nil ? true
process(): body == nil ? false
    INSIDE IsNil(): false
Run Code Online (Sandbox Code Playgroud)

如果你想要一个“统一”的IsNil()函数来判断其中一个是否是nil

func IsNil(i interface{}) bool {
    return i == nil || reflect.ValueOf(i).IsNil()
}
Run Code Online (Sandbox Code Playgroud)