Go新手踩坑指南:5个让代码变成地狱的错误

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么Go看起来简单,却让新手头疼?

\n\n

Go(或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()然后忘记处理错误。或者更糟,干脆不检查它:

\n\n
// 糟糕:错误被忽略\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%确定错误永远不会发生,否则永远不要用_来忽略错误。而在实际代码中,这种情况几乎不存在。

\n\n

错误#2:本应返回错误的地方却使用了恐慌

\n\n

Go中有panic和recover机制。新手常常把它们与异常混淆,一遇到问题就抛出恐慌。这是错误的。

\n\n

panic是程序的紧急停止。它用于程序无法继续运行的情况:例如解引用空指针或数组越界。用panic来处理业务逻辑是不合时宜的。

\n\n
// 糟糕:用恐慌代替错误\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不是没有原因的。

\n\n

错误#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++不是原子操作。它包含读取、增加和写入三个步骤。两个协程可能读取到相同的值,然后增加

博客

书籍推荐