背景
由于工作中大多使用 Go 语言,笔者在学习 Java 的同时也会兼顾一些 Go 语言的知识。这篇文章记录两个笔者见过 Go 语言中非常隐蔽的坑,如果不是提前见到,日后工作中大概率遇到后会调很久的 bug !
简单的 for range 循环,你真的了解吗?
话不多说,先来看一份代码。逻辑很简单,我们来猜猜看它的输出是什么?
import "fmt"
type Person struct {
name string
}
func main() {
persons := []Person{{"zhangsan"}, {"lisi"}}
newPersons := []*Person{}
for _, person := range persons {
newPersons = append(newPersons, &person)
}
println(newPersons[0].name)
println(newPersons[1].name)
}
或许读者期待的输出是
zhangsan
lisi然而事实上这段代码的输出是
lisi
lisipersonimport "fmt"
type Person struct {
name string
}
func main() {
persons := []Person{{"zhangsan"}, {"lisi"}}
newPersons := []*Person{}
for _, person := range persons {
fmt.Printf("add=%p \n", &person)
fmt.Println("name=" + person.name)
newPersons = append(newPersons, &person)
}
//println(newPersons[0].name)
//println(newPersons[1].name)
}
personadd=0xc000014250
name=zhangsan
add=0xc000014250
name=lisistatements.ccdo_lowergofrontend/go/statements.cc/For_range_statement::do_lower() // The loop we generate:
// for_temp := range
// len_temp := len(for_temp)
// for index_temp = 0; index_temp < len_temp; index_temp++ {
// value_temp = for_temp[index_temp]
// index = index_temp
// value = value_temp
// original body
// }rangevalue_tempvalue_tempnewPersons =append(newPersons,&person)newPersonsvalue_templisirangeerr !=nil 判断竟然失效了 ?
先来看一段代码,逻辑同样不复杂。
type MyError struct {
msg string
}
func (e *MyError) Error() string {
return e.msg
}
func checkError(e error) {
if e != nil {
println("false")
} else {
println("true")
}
}
func main() {
var err *MyError = nil
checkError(err) // e == nil ?
}
mainerre !=nil errvar err *NilError = nil
fmt.Printf("err=(%T,%v)", err, err)
// err=(*main.NilError,<nil>)
erre !=nilfmt.Printf("err=(%T,%v)", nil, nil)
// err=(<nil>,<nil>)
细心的读者已经发现了,本例中的 err 和 nil 的值一样,但类型不一样!
e !=nil我们去检查源码,发现 error 实际上是一个接口。
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}
e !=nil两个接口值相等,仅当它们都是 nil 值,或者它们的动态类型相同并且动态值也根据这个动态类型的 == 操作相等
知道原因后,如何避免这个问题发生?
首先,当我们作为 producer ,明确地需要返回一个空的 error 的时候,直接返回 nil
func returnError() error {
var e *NilError = nil
// return e
// do some processing
return nil
// return e - > don't do this
}
其次,当我们作为 consumer,需要检验 error 是否为空,可以参考本例的问题做出如下操作:忽略类型,检查 error 的值是否为空即可。
func IsNilError(c interface{}) bool {
return c == nil || reflect.ValueOf(c).IsNil()
}
更多细节可以参考这里