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
}
变量err为nil所以调用err.Error()方法会导致恐慌"运行时错误:无效的内存地址或nil指针取消引用",但方法调用成功。为什么不会出现这种恐慌?
答案 0 :(得分:5)
Michael Jones explained this well(复制为答案):
在Go中,Expression.Name()语法调用的函数是 完全取决于表达的类型而不是表达式 该表达式的特定运行时值,包括nil。
以这种方式,在一个nil指针上调用一个方法 具体类型具有明确的逻辑含义。
那些熟悉vtable []实现的人会发现这很奇怪 首先,当以这种方式思考方法时,它甚至更简单 说得通。想一想:
func (p *Sometype) Somemethod (firstArg int) {}
具有字面含义:
func SometypeSomemethod(p *Sometype, firstArg int) {}
在这个视图中,SometypeSomemethod()的主体肯定是免费的 测试它的(实际)第一个参数(p * Sometype)的值为nil。
答案 1 :(得分:3)
请阅读:https://golang.org/ref/spec#The_zero_value
通过声明为变量分配存储空间时 或者通过a来调用new,或者创建新值时 复合文字或make的调用,没有明确的初始化 提供,变量或值被赋予默认值。每个元素 将此类变量或值设置为其类型的零值: 布尔值为假,整数为0,浮点数为0.0,""对于字符串, 和 nil指针,函数,接口,切片,通道和 图即可。这种初始化是递归完成的,例如每个 如果没有值,则结构数组的元素将使其字段归零 已指定。
因此,在您的代码中,var err *MyError
为nil
,您可以致电err.Error()
即可err = nill
,
**
在Go中,Expression.Name()语法调用的函数是 完全取决于表达的类型而不是表达式 该表达式的特定运行时值,包括nil。
**
答案 2 :(得分:0)
来自C ++ / Java,这看起来很奇怪,另一个陷阱是拥有nil结构的接口本身并不是nil!
您可以在运动场上尝试一下以获取更多乐趣:
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")
}
}
您要注意的关键是:“继承”实际上是鸭子键入。接口被定义为可以容纳任何有效值,而nil本身就是有效值!