Go:哪些做对了,哪些做错了
Go 共同创造者 Rob Pike 的一篇回顾文章再次引发了关于这门语言在设计与演进中哪些做对、哪些做错的讨论。评论者普遍认为,Go 的快速工具链、强大的标准库、简单的并发原语,以及刻意保持小而稳定的核心,使它成为服务器和基础设施的流行选择。批评主要集中在较弱的 FFI 和科学计算支持、泛型引入长期拖延及其当前局限、普遍存在的 nil/error 陷阱、包与版本管理失误,以及一种被部分人视为忽视社区需求的领导风格,尤其是在错误处理、REPL、时间与加密 API 方面。
Go 的预期定位,以及它最终成了什么
- Go 由 Google 设计,目标是作为 C++ 更简单、更快速的替代品,用于服务器和“系统软件”(广义上指服务器、网络、基础设施),而不是用于内核或驱动程序。
- 一些评论者表示,Go 最终更多成了“服务器 / 互联网后端”以及 CLI 语言,而不是低层系统语言。
- 有人认为它确实在服务器/基础设施工作中取代了大量 C++ 和一部分 Python;也有人认为它更多是在与 Java/C# 竞争,而不是与 C/C++/Rust 竞争。
科学计算、FFI 与 HPC
- 据说 Go 早期团队轻视科学/HPC 用例、REPL,以及与脚本语言的集成;这被视为错失的机会。
- cgo 以及 gc 编译器调用 C 的开销受到广泛批评;Go 被认为在 FFI 方面较弱,尤其是在科学计算中,而 C/Fortran 互操作性至关重要。
- 反方观点:大多数科学工作是用 Python/R/Julia 做脚本;作为系统语言的 Go,本来就不是自然匹配。
并发与 goroutine
- Goroutine 被描述为用户空间的“绿色线程”或运行在 OS 线程之上的 M:N 调度、带栈 fiber;很适合高并发服务器。
- 有人认为 Go 把 goroutine 过度包装成新东西;也有人认为,尽管它类似更早的绿色线程系统,但作为一种实践上成功的模型,它依然很有价值。
- 这种并发模型让 C FFI 更复杂,也影响了运行时设计(例如:不能在运行时生成代码)。
错误处理、panic 与 nil
- 以错误值处理加 panic 是争议最大的领域之一:
- 批评者:
if err != nil模式冗长、重复;错误很容易被意外吞掉;存在两套并行机制(error 与 panic),却无法顺畅协同;nil 处理也很痛苦(包括 interface 与具体类型 nil 的陷阱)。 - 支持者:显式错误简单且可预测,鼓励处理错误,并避免隐藏的控制流。
- 批评者:
- nil 指针和缺乏空安全被视为主要设计错误;一些工具(如 linter、nil 分析器)试图缓解这个问题。
泛型与类型系统
- 在加入泛型之前拖延了很久,这一点引发了大量争论:
- 优点:等待避免了有缺陷、而且以后难以修复的设计。
- 缺点:在没有泛型的情况下发布重演了旧错误(例如 Java 在泛型之前的问题),扭曲了 API,并迫使开发者使用变通方案(到处都是
interface{})。
- 现有泛型被认为有用,但并不完整(例如缺少方法级泛型),而且受限于既有的 interface 语义。
模块、包管理与工具链
- 早期 GOPATH/
go get的行为以及按 URL 导入,被批评为天真,并且过度贴合 Google 的 monorepo;社区包管理器因此在这个空缺中成长起来。 - 官方 modules 和 MVS 一般被认为是重大改进,尽管 SIV(
v2+)、私有仓库,以及 SSH/git 配置仍然是痛点。 - gofmt、统一工具链(
go build/test/fmt)、静态二进制,以及相对较快的编译速度,被广泛视为主要优势。
社区流程与演进
- 一些人认为 Go 的领导层在 generics、单调时间、crypto 更新、REPL,以及 context 的使用等问题上过于轻视、推进过慢。
- 另一些人则赞赏其愿意说“不”、保持语言小巧,并优先考虑稳定性和工具链,而不是快速堆叠新特性。