英文:
Handle pointer to nil pointer in Go
问题
考虑以下简化的示例:
package main
import (
"fmt"
)
type IMessenger interface {
Message()
}
type TMyMessenger struct {
}
func (m TMyMessenger) Message() {}
func MessengerFactory() IMessenger {
return getInternalMessengerVariant()
}
func getInternalMessengerVariant() *TMyMessenger {
return nil
}
func main() {
e := MessengerFactory()
fmt.Println(" e == nil", e == nil) // *TMyMessenger(nil)
if e != nil {
e.Message()
}
}
它的输出结果为:
e == nil false
panic: runtime error: invalid memory address or nil pointer dereference
问题1:
在Go语言中,有没有一种惯用的方法来检查e
是否指向空指针?
最好是一种内联的代码片段。即使在示例情况下,使e != nil
为false
。
我考虑过的解决方案:
-
如果
getInternalMessengerVariant()
返回接口类型而不是具体指针类型,就不会出现这个问题,但这需要进行重构,而且仍然可能在运行时(如果e != nil
)引发恐慌。func getInternalMessengerVariant() IMessenger { return nil }
-
重写
MessengerFactory()
以拦截内部返回:func MessengerFactory() IMessenger { if m := getInternalMessengerVariant(); m != nil { return m } return nil }
-
对类型检查非常具体,但如果有很多类型怎么办:
if e != nil && e != (*TMyMessenger)(nil) { e.Message() }
英文:
Consider following simplified example:
package main
import (
"fmt"
)
type IMessenger interface {
Message()
}
type TMyMessenger struct {
}
func (m TMyMessenger) Message() {}
func MessengerFactory() IMessenger {
return getInternalMessengerVariant()
}
func getInternalMessengerVariant() *TMyMessenger {
return nil
}
func main() {
e := MessengerFactory()
fmt.Println(" e == nil", e == nil) // *TMyMessenger(nil)
if e != nil {
e.Message()
}
}
And it's output:
e == nil false
panic: runtime error: invalid memory address or nil pointer dereference
Question 1:
Is there an idiomatic Go way to check if e
points to a nil pointer?
Preferably an inline snippet.
Basically make the e != nil
to be false
even in the example case.
What I have considered:
-
There would not be this issue if
getInternalMessengerVariant()
would return Interface type instead of concrete pointer, but it requires refactor and may still go undetected and yield itself as a panic at runtime (if e != nil).func getInternalMessengerVariant() IMessenger { return nil }
-
Rewrite MessengerFactory() to intercept the internal returns:
func MessengerFactory() IMessenger { if m := getInternalMessengerVariant(); m != nil { return m } return nil }
-
Be very specific on type checking, but what if there are many types:
if e != nil && e != (*TMyMessenger)(nil) { e.Message() }
答案1
得分: 1
每当你从函数中返回一个接口时,就会出现这个问题:如果接口包含一个类型化的空指针,那么接口本身不是空。没有简单的方法来检查这一点。
处理这个问题的一个好方法是为接口返回一个空指针:
func MessengerFactory() IMessenger {
x := getInternalMessengerVariant()
if x == nil {
return nil
}
return x
}
这样你就不需要检查返回值是否指向一个空指针了。
英文:
This problem exists whenever you return an interface from a function: if the interface contains a typed nil-pointer, interface itself is not nil. There is no easy way to check that.
A good way to deal with this is to return a nil for the interface:
func MessengerFactory() IMessenger {
x:= getInternalMessengerVariant()
if x==nil {
return nil
}
return x
}
Then you will not need to check if the return value points to a nil pointer.
答案2
得分: 0
Burak Serdar在他的回答中很好地解释了为什么if x == nil
对你返回false
。
但这不是你出现panic的原因。
只要接收器不对指针进行解引用,Go就可以在nil
指针上调用接收器函数。
下面的代码不会引发panic:
type TMyMessenger struct {
}
func (m *TMyMessenger) Message() {}
func main() {
var t *TMyMessenger = nil
t.Message()
}
这是因为在Message
接收器函数内部没有对指针m
进行解引用。
你的示例之所以引发panic,是因为你在类型TMyMessenger
上定义了接收器函数m
(而不是指针)。因此,为了调用接收器函数,Go将不得不对IMessenger
接口值中的nil
指针进行解引用,该指针指向TMyMessenger
。
如果你在代码中更改一行,它将不再引发panic:
func (m *TMyMessenger) Message() {}
(将(m TMyMessenger)
更改为(m *TMyMessenger)
)
英文:
Burak Serdar explains well in his answer why if x == nil
returns false
for you.
But that is not the reason why you get the panic.
Go is happy to invoke a receiver function on a nil
pointer, as long as the receiver doesn't dereference the pointer.
This does not panic:
type TMyMessenger struct {
}
func (m *TMyMessenger) Message() {}
func main() {
var t *TMyMessenger = nil
t.Message()
}
and that's because you don't dereference the pointer m
inside the Message
receiver function.
Your example only panics because you have defined the receiver function m
on the type TMyMessenger
(not a pointer). Because of that, Go will have to dereference the nil
pointer to TMyMessenger
that is inside the IMessenger
interface value, in order to invoke the receiver function.
If you change one line in your code, it will no longer panic:
func (m *TMyMessenger) Message() {}
(change (m TMyMessenger)
to (m *TMyMessenger)
)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论