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

huangapple go评论87阅读模式
英文:

Why interface type doesn't provide an "IsNil" method?

问题

首先,让我们考虑以下代码:

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  // 你是否正确理解了这个结果?
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  // 你是否正确理解了这个结果?
container.Reader == nil ? true

关于这个问题的解释可以在这里找到:https://golang.org/doc/faq#nil_error。

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

然而,我想知道是否应该在所有接口类型上添加一个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)

你将得到一个空指针异常,尽管从源代码的外观来看,这不应该发生。

编辑

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

编辑2

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

英文:

First let's consider the following:

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

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\n", 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

得分: 5

首先阅读相关问题:https://stackoverflow.com/questions/29138591/hiding-nil-values-understanding-why-golang-fails-here/29138676#29138676

你可以通过将接口值与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()
}
英文:

First read this related question: https://stackoverflow.com/questions/29138591/hiding-nil-values-understanding-why-golang-fails-here/29138676#29138676

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

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

答案2

得分: 2

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

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

这是在 playground 中的一个可工作示例:https://play.golang.org/p/RHzu3VVj4Zd

英文:

Here's a function that reports if an interface contains any nil. Note that reflect.Value.IsNil() panics if the type doesn't have a nil value, so some checking on the kind must be done before calling it.

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

Here's a working example in the playground: https://play.golang.org/p/RHzu3VVj4Zd

答案3

得分: -1

以下是基于Paul Hankin的答案、icza的一点内容以及reflect/value.go源代码的翻译,其中定义了func (v Value) IsNil() bool。不要相信这个源代码,我是一个新手,正在利用坎宁安定律!我本来只想评论一下,但我连50个积分都没有(-:

func isNil(x interface{}) bool {
    if x == nil {                                 // 没有类型的nil。
        return true
    }
    v := reflect.ValueOf(x)
    switch v.Kind() {                             // nil的反射类型为reflect.Invalid
    case reflect.Chan, reflect.Func, reflect.Map, // v.isNil的第一个情况
        reflect.Ptr, reflect.UnsafePointer,       
        reflect.Interface, reflect.Slice:         // v.isNil的第二个情况
        return v.IsNil()
    default:
        return false
    }
}
英文:

The following is clearly based on Paul Hankin's answer, a tad of what icza wrote and the sources of reflect/value.go where func (v Value) IsNil() bool is defined. Do not trust the source, I am a newbie taking advantage of Cunningham's law! I would have just commented but I do not even have 50 points (-:

func isNil(x interface{}) bool {
	if x == nil {                                 // nil with no type.
		return true
	}
	v := reflect.ValueOf(x)
	switch v.Kind() {                             // reflect.Invalid for nil
	case reflect.Chan, reflect.Func, reflect.Map, // 1st case of v.isNil
	    reflect.Ptr, reflect.UnsafePointer,       
		reflect.Interface, reflect.Slice:         // 2nd case of v.isNil
		return v.IsNil()
	default:
		return false
	}
}

huangapple
  • 本文由 发表于 2017年5月10日 22:46:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/43895862.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定