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

时间:2017-05-10 14:46:32

标签: pointers go interface null

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

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

这是输出:

body == nil ? true
body == nil ? false  // Did you get this right?
body == nil ? true

另一个例子:

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

输出:

container.Reader == nil ? false // Did you get this right?
container.Reader == nil ? true

对此的解释是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)

你会得到一个nil指针异常,即使从源代码的外观来看,这不应该发生。

修改

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

修改2

我突出了我的问题,以明确我在问什么。

3 个答案:

答案 0 :(得分:3)

首先阅读此相关问题:Hiding nil values, understanding why golang fails here

您可以通过将界面值与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{})
}

输出(在Go Playground上尝试):

main(): body == nil ? true
process(): body == nil ? false
    INSIDE IsNil(): true
process(): body == nil ? true
process(): body == nil ? false
    INSIDE IsNil(): false

如果您想要一个“统一”IsNil()函数,该函数可以判断它们是nil

func IsNil(i interface{}) bool {
    return i == nil || reflect.ValueOf(i).IsNil()
}

答案 1 :(得分:2)

这是一个报告接口是否包含任何nil的函数。请注意,reflect.Value.IsNil()会在类型没有nil值时感到恐慌,因此在调用该类型之前必须进行一些检查。

func isNil(x interface{}) bool {
    v := reflect.ValueOf(x)
    switch v.Kind() {
    case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
        return v.IsNil()
    default:
        return false
    }
}

这是操场上的一个工作示例:https://play.golang.org/p/RHzu3VVj4Zd

答案 2 :(得分:-1)

以下内容显然基于Paul Hankin的回答,一点点icza的写作以及reflect / value.go的来源,其中定义了 Warning FailedMount 8s (x2 over 21s) kubelet, gke-windows-node-pool-e4e7a7bf-f2pc Unable to attach or mount volumes: unmounted volumes=[smb-volume], unattached volumes=[default-token-jf28b smb-volume]: failed to get Plugin from volumeSpec for volume "smb-volume" err=no volume plugin matched 。不相信消息来源,我是利用坎宁安法则的新手!我本来可以发表评论,但我什至没有50分(-:

func (v Value) IsNil() bool