Go语言编程踩坑
1 遇到的问题和排查
最近调试代码的时候遇到了类似下面这样的错误:
panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1095520]
通过日志,观察到error的值比较奇怪(前后不一致)。下面以一个示例代码来说明。
2 示例代码及说明
package main import ( "errors" "fmt" "log" ) // 示例的结构体 type S struct { a int } // main里的条件判断 func checkCondition() bool { return false } // main里的一个执行分支 func doOne() (*S, error) { return &S{a:1}, nil } // main里的另一个执行分支所需要的参数 func getParam() (string, error) { return "bad", nil } // main里的另一个执行分支 func doAnother(param string) (*S, error) { if param == "good" { return &S{a:2}, nil } else { return nil, errors.New("error in doAnother") } } // 主函数 func main() { var ( value *S err error ) // 检查条件是否满足 // 在我们的示例中,该条件总是不满足。之所以加上这个,是因为我们需要两个分支,由此产生将同样的返回值value和err在外部声明 if checkCondition() { value, err = doOne() } else { // 先获取所需参数 param, err := getParam() if err != nil { log.Fatalf("error occurs when getParam:% v\n", err) } // 执行第二个分支 value, err = doAnother(param) } // 一起判断上面两个分支的err if err != nil { log.Fatalf("error occurs when doThings: %v\n", err) } // 如果没有err,那么访问value的a字段 fmt.Println("value filed is", value.a) } // 运行结果: //panic: runtime error: invalid memory address or nil pointer dereference //[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x109d00b] // //goroutine 1 [running]: //main.main() ///Users/nisen/Projects/ethtools/test/demo.go:70 +0x4b //exit status 2 // 问题分析: // 问题出在了 := 赋值符,getParam() 里为了新使用的变量param采用:=来接受值的同时,由于处在else的{}里将err也重新申明了, // 这就导致了doAnother()返回的error复制到了这个新声明的err里(而不是整个if语句上面定义的error)。 // 这就导致了if语句下面对err检查的时候,doAnother(param)返回的错误值由于超出作用域无法获取到,所以这里的被检查的err是nil, // 这也是为什么在日志中观察到之前的err是存在的,到检查的时候就变成了nil。 // 这就导致了下面value.a实际上对于nil的解引用访问,导致了错误。 // 这个问题在网上搜索一下遇到的人不少,而且比较难发现,因为它和直觉有些违背。 // 对于操作符:=,如果新变量与那个同名已定义变量不在一个作用域中时,那么golang会重新定义这个变量。 // 有时候,前面统一定义了err,而后由于其他变量新定义的需求才采用了:=,副作用就是将以为统一不会变的err也改变了。 // 再加上跨作用域(if else等),导致错误不能捕捉到。