英文:
Calling a method on a nil struct pointer doesn't panic. Why not?
问题
以下是代码的中文翻译:
type MyError struct {
errors []string
}
func (t *MyError) Error() string {
if t == nil {
fmt.Println("t指针为空")
return ""
}
pointers := make([]string, 0, len(t.errors))
for i, r := range t.errors {
pointers[i] = r
}
sort.Strings(pointers)
return fmt.Sprintf("解码错误,共有 %d 个错误:\n\n%s", len(t.errors), strings.Join(pointers, ","))
}
func main() {
var err *MyError
err.Error() // 预期在这里引发 "panic: runtime error: invalid memory address or nil pointer dereference" 错误
}
变量err
是nil
,所以调用err.Error()
方法预期会引发"panic: runtime error: invalid memory address or nil pointer dereference"错误,但实际上方法调用成功了。为什么不会引发错误呢?
英文:
type MyError struct {
errors []string
}
func (t *MyError) Error() string {
if t == nil {
fmt.Println("t ptr empty")
return ""
}
pointers := make([]string, 0, len(t.errors))
for i, r := range t.errors {
pointers[i] = r
}
sort.Strings(pointers)
return fmt.Sprintf("n error(s) decoding:\n\n%s", len(t.errors), strings.Join(pointers, ","))
}
func main() {
var err *MyError
err.Error() // expected "panic: runtime error: invalid memory address or nil pointer dereference" here
}
The variable err is nil so calling err.Error() method is expected to cause a panic "runtime error: invalid memory address or nil pointer dereference", but the method call succeeds. Why doesn't this panic?
答案1
得分: 29
Michael Jones解释得很好(作为答案复制):
在Go语言中,通过Expression.Name()语法调用的函数完全由Expression的类型确定,而不是由该表达式的特定运行时值确定,包括nil。
以这种方式,在特定类型的nil指针上调用方法具有明确且逻辑清晰的含义。
熟悉vtable[]实现的人可能会觉得这很奇怪,但是,当以这种方式思考方法时,它甚至更简单且合乎逻辑。可以将:
func (p *Sometype) Somemethod (firstArg int) {}
理解为具有字面意义的:
func SometypeSomemethod(p *Sometype, firstArg int) {}
在这个视角下,SometypeSomemethod()的函数体当然可以自由地测试它的(实际)第一个参数(p *Sometype)是否为nil。
英文:
Michael Jones explained this well (copied as answer):
> In Go the function to be called by the Expression.Name() syntax is
> entirely determined by the type of Expression and not by the
> particular run-time value of that expression, including nil.
>
> In this manner, the invocation of a method on a nil pointer of a
> specific type has a clear and logical meaning.
>
> Those familiar with vtable[] implementations will find this odd at
> first, yet, when thinking of methods this way, it is even simpler and
> makes sense. Think of:
>
> func (p *Sometype) Somemethod (firstArg int) {}
>
> as having the literal meaning:
>
> func SometypeSomemethod(p *Sometype, firstArg int) {}
>
> and in this view, the body of SometypeSomemethod() is certainly free
> to test it's (actual) first argument (p *Sometype) for a value of nil.
答案2
得分: 8
请阅读:https://golang.org/ref/spec#The_zero_value
当为变量分配存储空间时,无论是通过声明还是通过new的调用,或者当通过复合字面量或make的调用创建新值时,如果没有提供显式初始化,变量或值将被赋予默认值。这样变量或值的每个元素都将设置为其类型的零值:布尔类型为false,整数类型为0,浮点数类型为0.0,字符串类型为"",指针、函数、接口、切片、通道和映射类型为nil。这种初始化是递归进行的,因此,例如,如果未指定值,则结构体数组的每个元素的字段将被清零。
因此,在您的代码中,var err *MyError
是 nil
,即使 err = nil
,您仍然可以调用 err.Error()
。
在Go中,通过Expression.Name()语法调用的函数完全由Expression的类型确定,而不是由该表达式的特定运行时值确定,包括nil。
英文:
Please read: https://golang.org/ref/spec#The_zero_value
> When storage is allocated for a variable, either through a declaration
> or a call of new, or when a new value is created, either through a
> composite literal or a call of make, and no explicit initialization is
> provided, the variable or value is given a default value. Each element
> of such a variable or value is set to the zero value for its type:
> false for booleans, 0 for integers, 0.0 for floats, "" for strings,
> and nil for pointers, functions, interfaces, slices, channels, and
> maps. This initialization is done recursively, so for instance each
> element of an array of structs will have its fields zeroed if no value
> is specified.
So in your code, var err *MyError
is nil
, you can call err.Error()
is ok even err = nill
,
**
> In Go, the function to be called by the Expression.Name() syntax is
> entirely determined by the type of Expression and not by the
> particular run-time value of that expression, including nil.
**
答案3
得分: 0
从C++/Java过来,这可能看起来很奇怪,另一个需要注意的地方是,持有空结构体的接口本身并不是空!
你可以在Go Playground上尝试一下:
type inf interface {
test() string
}
type s struct {
}
func (t *s) test() string {
return "nil struct with pointer receiver can still call method\n"
}
func main() {
var sTest *s
if sTest == nil {
fmt.Printf("sTest is Nil\n")
}
fmt.Printf("%s", sTest.test())
var fTest inf
if fTest == nil {
fmt.Printf("fTest is nil\n")
}
fTest = sTest
if fTest != nil {
fmt.Printf("interface holding nil is not nil!\n")
}
}
对你来说,关键的一点是,Go的“继承”实际上是鸭子类型。接口被定义为能够持有任何有效值,而nil本身就是一个有效值!
英文:
Coming from C++/Java, this can look odd, another gotcha is that an interface holding a nil struct is not itself nil!
You can try this on go playground to get a hang of things:
type inf interface {
test() string
}
type s struct {
}
func (t *s) test() string {
return "nil struct with pointer reciever can still call method\n"
}
func main() {
var sTest *s
if sTest == nil {
fmt.Printf("sTest is Nil\n")
}
fmt.Printf("%s", sTest.test())
var fTest inf
if fTest == nil {
fmt.Printf("fTest is nil\n")
}
fTest = sTest
if fTest != nil {
fmt.Printf("interface holding nil is not nil!\n")
}
}
The key thing to you: go 'inheritance' is actually duck typing. And interfaces are defined to be able hold any valid value, and nil itself is a valid value!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论