当前位置: 首页 > 后端技术 > PHP

你犯过这些Go编码错误吗(第二部分)?

时间:2023-03-29 21:03:53 PHP

大家好,我是炸鱼。上次跟大家分享了《你会犯这些 Go 编码错误吗(一)?》。不知道你吸收的怎么样。会不会再踩类似的坑?今天继续第二弹,炸鱼上车。Go常见错误六、同名变量的作用域问题我们在编写程序的时候,由于存在各种临时变量,所以经常会用到n、i、err等变量名。有时候你会遇到一些问题,比如下面的代码:funcmain(){n:=0iftrue{n:=1n++}fmt.Println(n)}程序的输出是什么。n是1还是2?输出结果:0解法n:=1上面代码重新声明了一个新的变量,这个变量是一个同名的变量,关键是它包含一个同名的局部变量。我们的n++影响if块内的变量n,而不是同名的外部变量n。如果要正确影响,应该改为:funcmain(){n:=0iftrue{n=1n++}fmt.Println(n)}outputresult:2虽然这个case在a中并不复杂单介绍,就是很多Go初学者在刚入门的时候在应用中经常会遇到类似的问题,然后问为什么不行。。。据余7s的记忆,他在全局DB句柄等遇到过这个问题场景。试问建宇应该有10次以上,属于比较高频的“坑”。7.循环中临时变量的问题。相信很多同学在业务代码中都做过类似的事情,就是:在循环处理业务数据的同时,改变值的内容。如下代码:s:=[]int{1,1,1}for_,n:=ranges{n+=1}fmt.Println(s)程序输出的结果是什么,i是+1为了成功?不,真正的输出是:[111]。解决方法其实是在循环中,我们引用的变量是一个临时变量,你修改它是没有意义的,你修改的根本不是你原始数据的结构。我们需要定位原始数据,根据索引定位修改。如下代码:s:=[]int{1,1,1}fori:=ranges{s[i]+=1}fmt.Println(s)输出结果:[222]这个很常见,一不留神,手就会抖。8、JSON转换和输出空的问题在做对外接口的数据连接和转换的时候,我们经常需要处理JSON数据。以下代码:typeTstruct{namestringageint}funcmain(){p:=T{"Friedfish",18}jsonData,_:=json.Marshal(p)fmt.Println(string(jsonData))}输出结果是什么?名字和年龄能正常输出吗?输出:{}你没看错,程序的输出是空的,没有转换成任何东西。解决方案的原因是json的输出只会输出public(exported)字段,即首字母必须大写。我们需要进行以下转换:typeTstruct{NamestringAgeint}funcmain(){p:=T{"Friedfish",18}jsonData,_:=json.Marshal(p)...}output结果:{"Name":"FriedFish","Age":18}或显式JSON标签:typeTstruct{Namestring`json:"name"`Ageint`json:"age"`}IDE可以轻松直接生成JSON标签。建议大家可以习惯性添加,保证现场规范。确实可以避免很多对接时的拉扯和错误。9.认为recover是灵丹妙药的问题在Go中,goroutine+panic+recover是天作之合,用起来非常方便。经常有同学认为他无所不能。以下代码:funcgohead(){gofunc(){panic("Friedfishisoffwork")}()}funcmain(){gofunc(){deferfunc(){ifr:=recover();r!=nil{fmt.Println(r)}}()gohead()}()time.Sleep(time.Second)}你认为输出会是什么。是程序中断了,还是成功恢复了?输出:panic:friedfishisoffworkgoroutine17[running]:main.gohead.func1()/Users/eddycjy/awesomeProject/main.go:10+0x39createdbymain.gohead/Users/eddycjy/awesomeProject/main.go:9+0x35你以为recover是万能的吗?不是。解决方案Go1现阶段没有通用的解决方案,只能遵守Go的规范(Go不能跨goroutines恢复,这是正确的逻辑)。goroutine建议每个goroutine都需要有recovery覆盖,否则一旦发生panic,应用会中断,容器会重启。另外,Go底层抛出的fatalerror也没有办法使用recover来拦截,请注意。注:部分同学一直认为recover可以拦截throw,特此说明。10.nil不为nil的问题我们在做程序的逻辑处理时,往往要判断接口(interface)的值。以下代码:funcFoo()error{varerr*os.PathError=nilreturnerr}funcmain(){err:=Foo()fmt.Println(err)fmt.Println(err==nil)}做了什么你认为输出的结果是什么,nil和true?输出:false表面上看是nil,其实不等于nil。解决方法接口值是特殊的。只有当它的值和动态类型都为nil时,接口值才等于nil。在前面的问题代码中,函数Foo实际上返回了[nil,*os.PathError],我们将其与[nil,nil]进行比较,因此它是假的。如果要准确判断,需要进行如下转换:fmt.Println(err==(*os.PathError)(nil))输出结果为真。另外就是尽量使用error类型,或者避免与interface比较,这是一种危险的行为(很多人不知道这种现象)。总结在今天的帖子中,我们开始了Go常见编码错误的第二部分,共涵盖了5种情况:同名变量的作用域。循环中的临时变量。JSON转换和输出为空。我以为恢复是无所不能的。零不是零。这还是很常见的情况,你遇到过吗?还是有其他新病例?欢迎大家一起交流交流。文章持续更新中。可以微信搜索【脑补炸鱼】阅读。本文已收录在GitHubgithub.com/eddycjy/blog中。学习Go语言可以看Go学习地图和路线。欢迎星星提醒。推荐阅读《Go语言入门系列:初探Go项目实战Go语言编程之旅:深入使用Go作为项目:Gogotchascasefrom24CommonMistakesinGo(gotchas)AndHowToAvoidThem》