为什么Go看起来简单,却让新手头疼?
\n\nGo(或Golang)是一门旨在让程序员少受苦的语言。简洁的语法、内置并发、快速编译——看起来一周就能学会。没错,学会它确实不难。但“写出好代码”和“写出Go代码”是两回事。
\n\n新手常常把Python、JavaScript或Java的习惯带到Go中,试图生搬硬套。结果就是难以排查的bug、运行时恐慌,以及一种“Go是不是坏了”的感觉。剧透:Go没坏。只是它的思维方式不同。
\n\n本文将剖析学习Go时最常见的5个错误。如果你刚开始学习,请仔细阅读。如果你已经在用Go写代码,也请检查一下自己是否还在犯这些错误。
\n\n错误#1:忽略错误处理(以及来自Python的习惯)
\n\n在Python或JavaScript中,你习惯了函数要么返回结果,要么抛出异常。你用try/catch包裹调用,然后安心生活。Go则完全不同:这里没有异常。只有返回值,而其中之一就是错误。
\n\n新手最常见的错误是写result, _ := someFunc()然后忘记处理错误。或者更糟,干脆不检查它:
// 糟糕:错误被忽略\nfile, _ := os.Open("config.yaml")\n// 如果文件不存在——file == nil,下一行代码会引发恐慌\ndefer file.Close()\n\n\n在Go中,错误并不可怕。它只是一个值。你需要检查它。始终如此。无一例外。
\n\n正确做法是什么?
\n\n// 良好:立即检查错误\nfile, err := os.Open("config.yaml")\nif err != nil {\n log.Fatalf("无法打开文件: %v", err)\n}\ndefer file.Close()\n\n\n这不是偏执。这是纪律。Go教会你要严谨。如果你在生产环境中忽略了错误,那就准备好凌晨3点服务崩溃,而日志中却一片寂静吧。
\n\n建议:除非你100%确定错误永远不会发生,否则永远不要用_来忽略错误。而在实际代码中,这种情况几乎不存在。
错误#2:本应返回错误的地方却使用了恐慌
\n\nGo中有panic和recover机制。新手常常把它们与异常混淆,一遇到问题就抛出恐慌。这是错误的。
panic是程序的紧急停止。它用于程序无法继续运行的情况:例如解引用空指针或数组越界。用panic来处理业务逻辑是不合时宜的。
// 糟糕:用恐慌代替错误\nfunc divide(a, b int) int {\n if b == 0 {\n panic("除以零!")\n }\n return a / b\n}\n\n\n正确的做法是返回错误:
\n\n// 良好:返回错误\nfunc divide(a, b int) (int, error) {\n if b == 0 {\n return 0, fmt.Errorf("除以零")\n }\n return a / b, nil\n}\n\n\n只在真正危急的情况下使用panic。其他所有情况都应该是错误。Go给你error不是没有原因的。
错误#3:协程使用不当且缺乏同步
\n\n协程可以说是Go最酷的特性。但它对新手来说也是最危险的。典型场景:启动1000个协程,写入一个共享变量——然后你就得到了数据竞争。
\n\n// 糟糕:数据竞争\nvar counter int\nfor i := 0; i < 1000; i++ {\n go func() {\n counter++ // 不安全!\n }()\n}\ntime.Sleep(time.Second)\nfmt.Println(counter) // 结果不可预测\n\n\n为什么这是个问题?因为counter++不是原子操作。它包含读取、增加和写入三个步骤。两个协程可能读取到相同的值,然后增加