以下代码是否会造成程序panic?
package main import "fmt" type Table struct { name string } func (t *Table) TableName() string { return t.name } func main() { var table *Table fmt.Println(table.TableName()) }
毫无疑问,会!
因为table只是声明了变量,但是没有赋值,此时值为零值nil,在调用对应方法时会造成nil dereference panic。
那下面的代码会panic吗?
package main import "fmt" type Table struct {} func (t *Table) TableName() string { return "hello_world" } func main() { var table *Table fmt.Println(table.TableName()) }
答案是,不会!
为啥呢?因为TableName方法里,没有访问Table类型的成员变量,因此不涉及解引用,所以不会panic。
其实一个方法(method)等价于第一个参数为receiver的函数(function),例如:
type Table struct {} func (t *Table) TableName() string { return "hello_world" }
其实等价于
type Table struct {} func TableName(t *Table) string { return "hello_world" }
这时候再看,就很明白了,作为参数的时候,t可以为nil吗?可以。会panic吗?不会。因此等价的方法也不会panic。
再一个,真正引起panic的是对nil指针进行解引用操作,如果没有解引用,那么也不会造成panic。解引用就是去寻找一个内存地址对应的真正数据,在找之前会判断地址是否有效,若无效,则就会引起panic。
比如下面的方法:
type Pointer struct{} func (p *Pointer)PrintAddr() { fmt.Sprint("addr: %p", p) } // output: addr: 0x0
以上的方法,虽然访问了p,并且p也是nil,但是因为没有解引用,所以没有panic。
那么问题来了,以下的代码会造成panic吗?
package main import "fmt" type Table struct {} func (t Table) TableName() string { return "hello_world" } func main() { var table *Table fmt.Println(table.TableName()) }
答案是:会!
这里为啥又会了呢?
按照上面等价理论来说,确实是不会,但是需要注意的一点是,以指针类型去调用非指针类型方法时,是会自动对指针进行解引用的,因此还是会造成panic。用以上等价理论解释就是等价于以下代码:
type Table struct {} func TableName(t Table) string { return "hello_world" } func main() { var pointer *Table table = *pointer fmt.Println(TableName(table)) }
真正导致panic的地方是在main中的第二行代码,而并非是在TableName中。
总结,nil receiver是否会造成panic,主要取决于是否对其进行了解引用操作,如果有,则会panic,如果没有,则不会引起panic。