dpjjr42626 2017-05-10 14:46
浏览 41
已采纳

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

First let's consider the following:

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

func main() {
    var body *bytes.Buffer
    fmt.Printf("body == nil ? %+v
", body == nil)
    process(body)
    process(nil)
}

And here's the output:

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

Another example:

type Container struct {
    Reader io.Reader
}

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

func main() {
    var body *bytes.Buffer
    processContainer(Container{Reader: body})
    processContainer(Container{Reader: nil})
}

Output:

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

The explanation for this is at https://golang.org/doc/faq#nil_error.

A naive solution is to make the == nil test just return true if the interface object contains nil value. But this would violate transitivity of ==, as it would assert two objects with nil value but different interface types true under ==.

However, I wonder if there should be an IsNil() method on all interface types, which would solve this issue?

Another example, this line from Go http client, can catch you unexpectedly:

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

So if you call it like this

var body *bytes.Buffer
http.NewRequest(method, path, body)

You'll get a nil pointer exception, even though, by the look of the source code, this shouldn't happen.

Edit

Sorry I referenced the wrong line of the Go http source, now corrected. But the example still holds.

Edit 2

I've highlighted my question to make it clear what I'm asking.

  • 写回答

1条回答 默认 最新

  • douxi8119 2017-05-10 15:00
    关注

    First read this related question: Hiding nil values, understanding why golang fails here

    You can check if the interface value itself is nil by comparing it to nil.

    If you want to check if the value wrapped inside a non-nil interface is nil, you can use reflection: reflect.Value.IsNil().

    See this modified example:

    func process(body io.Reader) {
        fmt.Printf("process(): body == nil ? %t
    ", 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
    ", body == nil)
        process(body)
        process(nil)
        process(&bytes.Buffer{})
    }
    

    Output (try it on the Go Playground):

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

    If you want a "unified" IsNil() function which tells if either is nil:

    func IsNil(i interface{}) bool {
        return i == nil || reflect.ValueOf(i).IsNil()
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 关于CMOS电路图的提问
  • ¥15 Verilog hdl密码锁设计
  • ¥35 基于python的有ssl加密传输的socket聊天室
  • ¥15 数码管亮度控制器设计
  • ¥15 kafka客户端跨网段访问,看日志提示连接的还剩内网地址,且访问不通
  • ¥15 关于c语言代码的问题
  • ¥15 c51单片机控制步进电机
  • ¥20 Visual studio无法检测到设备
  • ¥30 vue 页面窗口放大或者缩小元素会变化
  • ¥15 questasim仿真报错