大家好,我是建宇。在用Go编程的时候,总会遇到各种奇怪的错误。国内外很多小伙伴都总结过(参考链接见参考资料),感觉都可以做个表格。我希望能有所帮助。Go常见错误1.nilMap问题在程序中声明(定义)一个map,然后直接写入数据。代码如下:funcmain(){varmmap[string]stringm["friedfish"]="Igotintomybrain"}输出结果:panic:赋值给nilmap中的entry会直接抛出panic.解决方案解决方案是声明和初始化。Go的标准写法是调用make函数。如下代码:funcmain(){m:=make(map[string]string)m["friedfish"]="offwork"}这个问题是学习围棋最容易踩到的错误。2.空指针引用问题我们在Go中经常使用结构体来声明一系列的方法。它看起来像面向对象中的一个“类”,在业务代码中很常见。以下代码:typePointstruct{X,Yfloat64}func(p*Point)Abs()float64{returnmath.Sqrt(p.X*p.X+p.Y*p.Y)}funcmain(){varp*Pointfmt.Println(p.Abs())}这个程序能正常运行吗?正常计算和输出?输出结果:panic:runtimeerror:invalidmemoryaddressornilpointerdereference[signalSIGSEGV:segmentationviolationcode=0x1addr=0x0pc=0x10a3143]goroutine1[running]:main.(*Point).Abs(...)/Users/eddycjy/awesomeProject/main.go:13main.main()/Users/eddycj/awesomeProject/main.go:18+0x23直接panic了,引用了空指针。解决方案如果变量p是一个指针,那么在调用之前必须先初始化它。以下代码:funcmain(){varp*Point=new(Point)fmt.Println(p.Abs())}或使用值对象方法解决:funcmain(){varpPoint//haszerovaluePoint{X:0,Y:0}fmt.Println(p.Abs())}3.使用循环迭代器变量的引用问题在Go中,循环迭代器变量是一个不同值的单一变量在循环迭代中。如果使用不当,这可能会导致意外行为。以下代码:funcmain(){varout[]*intfori:=0;我<3;i++{out=append(out,&i)}fmt.Println("值:",*out[0],*out[1],*out[2])fmt.Println("地址:",out[0],out[1],out[2])}输出什么。大胆猜测值是1、2、3,地址都不同。正确的?Output:Values:333Addresses:0x40e0200x40e0200x40e020值都是3,地址都指向同一个点。一种解决方法是将循环变量复制到一个新变量中:fori:=0;我<3;i++{i:=i//将i复制到一个新变量中。out=append(out,&i)}Output:Values:012Addresses:0x40e0200x40e0240x40e028原因是:在每次迭代中,我们将i的地址附加到out切片,但由于它是同一个变量,我们实际上附加相同最终包含分配给i的最后一个值的地址。所以你只需要制作一份,让两者分开即可。4、在循环迭代器变量上使用goroutine的问题在Go中循环的时候,我们经常会使用goroutines来并发处理数据。最经典的就是结合闭包写业务逻辑。如下代码:values:=[]int{1,2,3,4,5}for_,val:=rangevalues{gofunc(){fmt.Println(val)}()}time.Sleep(time.Second)但是在实际操作中,上面的for循环可能并不能达到你的预期,你想要的可能是顺序输出切片中的值。输出结果为:55455你可能会看到每次迭代都打印出最后一个元素,甚至你会发现每次输出的结果都不一样...如果去掉sleep代码,你会发现goroutine可能不一样开始执行,程序结束。解决方案这其实是闭包使用中的一个常见问题。闭环的正确写法是:values:=[]int{1,2,3,4,5}for_,val:=rangevalues{gofunc(valint){fmt.Println(val)}(val)}通过将val作为参数添加到闭包中,变量val会在每次循环时存储在goroutine栈中,以确保最终goroutine的值在执行时是正确的。当然,这里隐含了一个问题。大家总是认为1、2、3、4、5是按顺序输出的。其实并不是这样的,因为goroutine的执行是随机的,不能保证顺序。注意:经常变形,出现在很多围棋面试题中。一旦复杂了,就很容易让人迷惑。5.数组不会改变。问题切片和数字是Go程序中使用最广泛的数据类型,但它们经常会出现一些奇怪的问题。以下代码:funcFoo(a[2]int){a[0]=8}funcmain(){a:=[2]int{1,2}Foo(a)fmt.Println(a)}输出它是什么。是[82],对吧?输出结果:[12]这是为什么,函数被修改了?解决方案其实在Go中,所有的函数传递都是值传递。也就是说,当一个数组被传递给一个函数时,这个数组被复制了。如果确实需要传入函数进行修改,可以使用切片代替。以下代码:funcFoo(a[]int){iflen(a)>0{a[0]=8}}funcmain(){a:=[]int{1,2}Foo(a)fmt.Println(a)}outputresult:[82]原因是:切片不会存储任何数据,它的底层数据会指向一个底层数组。因此,当修改一个切片的元素时,其底层数组对应的元素也会被修改,共享同一底层数组的其他切片也会一起被修改。你认为一切都会好起来吗?没有。当slice展开时,Go的底层会重新申请一个新的更大的空间,也有和原来的slice分离的场景。所以还是需要及时返回变化的值,元数据统一在主进程处理会更好。小结今天的文章,我们开始了Go语言常见编码错误的第一节,共5个案例:nilMap。对空指针的引用。使用对循环迭代器变量的引用。在循环迭代器变量上使用goroutines。数组不会改变。这些情况非常常见,在查看单个代码时更容易发现。但是一旦混入应用程序中,就很难在混乱的代码中看到。祝大家吸烟后少走弯路,少走弯路。请参阅golang/go/wiki/CommonMistakes24Go中的常见错误(陷阱)以及如何避免它们
