英文:
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
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论