> ## Content Index
> Fetch the complete content index at: https://huizhou92.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# [译]事实上,Go 是一种设计良好的语言
- URL: https://huizhou92.com/e8-af-91-e4-ba-8b-e5-ae-9e-e4-b8-8ago-e6-98-af-e4-b8-80-e7-a7-8d-e8-ae-be-e8-ae-a1-e8-89-af-e5-a5-bd-e7-9a-84-e8-af-ad-e8-a8-80/
- Published: 2025-01-09T10:42:34.000Z
- Updated: 2026-09-06T11:41:14.000Z
- Description: [译]事实上,Go 是一种设计良好的语言. 原文链接： Go is a Well-Designed Language, Actually 哈哈，没有泛型 —— 这是一句古老的程序员谚语。 从诸多方面来看，2009 年为我未来的职业生涯埋下了伏笔。那时我 13 岁，刚在一场足球赛里打进了人生中的第一粒进球。那是。
- Author: huizhou92
- Tags: tech, golang, #lang-zh-cn

原文链接：[Go is a Well-Designed Language, Actually](https://mattjhall.co.uk/posts/go-is-well-designed-actually.html?ref=huizhou92.com)

> 哈哈，没有泛型 —— 这是一句古老的程序员谚语。

从诸多方面来看，2009 年为我未来的职业生涯埋下了伏笔。那时我 13 岁，刚在一场足球赛里打进了人生中的第一粒进球。那是一次精彩的二过一配合，最后我一记大力抽射，球直入球门左上角。可惜的是，那天球探不知去向。当我还憧憬着踏入温布利球场的那一刻，Go 语言诞生了。

Go 语言很快就吸引了大批拥趸。大家钟情于它的简洁性，还有它对 Web 服务的优化，以及像 `gofmt` 这类实用工具。不过，凡事皆有两面，Go 语言也不例外。有人嫌弃它太过简单，抱怨它只能用来捣鼓蹩脚的 REST API，还吐槽那些过于 “热情” 的工具。

在过去的 15 年里，人们写下了大量对 Go 语言的批评，甚至是愤怒的吐槽。其中让我格外留意的，是有人认为 Go 语言设计得很糟糕。这一观点在两篇文章里体现得尤为明显 —— 《[我想摆脱 Golang 先生的狂野之旅》](https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-ride?ref=huizhou92.com) 和 [《我们告诉自己继续使用 Go 的谎言](https://fasterthanli.me/articles/lies-we-tell-ourselves-to-keep-using-golang?ref=huizhou92.com)》，均出自 fastthanlime 之手。后者更是直言：

> 所以他们没有。他们没有设计语言。它就这么 “冒出来” 了。

## 设计究竟是什么？

在我看来，设计就是达成目标的计划或规范。打个比方，BBC 新闻网站的目标是向用户通报全球发生的、与他们切身相关的大事。为实现这一目标，网站会撰写新闻报道，再依据事件发生地和重要程度进行排序。毕竟，一枚朝我飞来的核弹，可比一只挂在树上的猫要紧要得多。

所以，判断一个设计好不好，要看它能在多大程度上实现既定的设计目标。

## Go 语言的起源

Go 语言诞生于谷歌，Russ Cox、Rob Pike、Ken Thompson 等众多大咖都效力于谷歌。彼时，谷歌内部主要使用 Java 和 C++。Go 语言的设计者们觉得，这两门语言性能虽优，但用起来实在费劲。编译器慢吞吞的，工具还特别挑剔，而且它们的设计至少都是十年前的老黄历了。与此同时，云计算 —— 大量多核服务器协同作业，正变得日益普及。

于是，他们决定打造一门属于自己的语言，优先考虑让它能在大规模的计算任务以及人力协作方面游刃有余。Rob Pike 在 [Go at Google](https://go.dev/talks/2012/splash.article?ref=huizhou92.com) 一文中解释道：

> 硬件规模庞大，软件亦是如此。软件动辄数百万行代码，服务器端大多用 C++ 编写，其余部分则大量采用 Java 和 Python。数千名工程师投身于代码编写工作。

在别的场合，Rob Pike 一如既往地以谦逊、含蓄的口吻谈及他所面向的那数千名工程师：

> 关键在于，我们的程序员是谷歌员工，而非科研人员。高深精妙的语言，他们可玩不转。

*重要提示：要是你正在搞设计，千万要避免贬低、居高临下地对待你的设计受众。*

尽管有这么一段引发争议的言论，不过我们还是能看出一个相当合理的设计目标：**这门语言得让编写和维护大型并发服务器代码变得轻松容易，哪怕使用者是数千名技能水平参差不齐的开发人员**。

## 针对 Go 语言的批评

咱们来瞧瞧人们对 Go 语言的一些怨言，再依据它的设计目标来评判一番。

### 文件系统 API

Go 语言的文件系统 API 常常遭人诟病，原因是它太偏向 Unix 系统了。Windows 系统不像 Unix 那样有文件权限一说，所以 Go 语言只能返回一些形同虚设的权限。而且，Go 对路径的处理相当简单粗暴。操作系统有自己的路径分隔符，而路径在 Go 里就是 `string` 类型 —— 仅仅是一串字节，没有任何实质性的检查或限制（译者注：在 go 1.24 版本中使用 `os.ROOT` 会改善不少）。

其他语言在这方面就严谨得多。比如 [在 Rust 里获取文件修改时间的方法](https://doc.rust-lang.org/std/fs/struct.Metadata.html?ref=huizhou92.com#method.modified)，有可能返回 `None`。Zig 语言里 [文件的元数据会因操作系统而异](https://ziglang.org/documentation/master/std/?ref=huizhou92.com#std.fs.File.Metadata)。

不过从设计目标的角度来看，这倒也情有可原。Go 语言本就是为谷歌量身打造的，和大多数服务器一样，谷歌的服务器 [清一色用的是 Linux](https://workspace.google.com/learn-more/security/security-whitepaper/page-4.html?ref=huizhou92.com)。要是你设计一门主打服务器应用的语言，以 Unix 为核心来打造文件系统 API，不失为一个明智之举。

### 无运算符或函数重载

在 Go 语言里，和 Java 不同，函数和方法只有单一的定义（一旦指定了构建标签和目标）。与 C++ 迥异的是，运算符是在编译器里预先实现好的，无法重载。在 `time` 包里，要是想把 `Duration` 类型的值加到 `Time` 类型上，得用 `Add` 方法。要是你想增加两天，可不能像这样调用 `Add(0 /*years*/, 0 /*months*/, 2 /*days*/)`，而得用 `AddDate` 方法。

在有些人眼里，这显得不够优雅，但它胜在简洁明了。在 Go 代码里看到函数调用，你心里清楚只需查看一处定义就行。要是瞅见一个运算符，你也明白它是针对内置类型的，干的肯定是靠谱的事儿，绝不会是 [铸造 NFT 这种奇葩操作](https://github.com/zhuowei/nft%5Fptr?tab=readme-ov-file&ref=huizhou92.com)。

### 费力的错误处理

公允地讲，当下编程语言的潮流是追求简洁。也难怪程序员们都反感 Go 语言里那种 `if err!= nil` 的错误处理风格。

然而，这也是深思熟虑后的抉择：

> 虽说相比之下，Go 语言检查错误的写法更啰嗦，但这种显式设计让控制流程一目了然 —— 就是字面意义上的清晰。

清晰明了的控制流程让代码的可读性更强。虽说支持异常处理的语言写起代码来可能更快，但生成的代码没那么简洁，而且控制流程藏得很深。

Go 语言常常因避开异常处理这类特性而饱受批评，有人觉得这简直是开倒车。曾经有人质问设计者：“为啥你们对 20 世纪 70 年代以来有关类型系统的研究成果一概无视？”。类似的论调 [在别处也屡见不鲜](https://www.bluxte.net/musings/2018/04/10/go-good-bad-ugly/?ref=huizhou92.com#go-ignored-advances-in-modern-language-design)。

首先，Rob Pike 可瞧不上这种傲慢，也压根儿不 care：

> Go 旨在解决谷歌在软件开发过程中遭遇的难题，这就使得这门语言虽说算不上开创性的科研语言，但用来搞大型软件项目，那绝对是把好手。

其次，将错误设计成明确的值，已然成为一种（再度）引领潮流的做法。Go、Rust 和 Zig 都选用了这种方式。Swift 语言即便支持异常，也要求你在函数签名里标明哪些函数可能会出错。

### 可怜的 FFI 能力

> 译者注：FFI，中文名叫语言交互接口 (Foreign Function Interface)，指的是能在某种计算机语言里调用其他语言的接口。

Go 语言与其他语言的兼容性欠佳。要是你想调用 C 函数，比如使用 SQLite，那就得通过 CGO。[要知道，CGO 可不是纯正的 Go](https://dave.cheney.net/2016/01/18/cgo-is-not-go?ref=huizhou92.com)，还存在性能损耗。由于 goroutine（拥有由 Go 运行时设定的专属堆栈）是执行单元，Go 就得按照 C 语言的期望来做一些操作以获取堆栈，这成本可不低。

Go 语言的 FFI 表现不佳，还因为它有自己的编译器、链接器和调试器。Go 生态系统里的好多东西都是定制化的。

不过，考虑到设计目标的话，这也说得通。服务器软件必须支持并发，所以采用了 goroutine。这必然会让调用 C 代码变得复杂些，但这种权衡利弊，至少适配 Go 用于服务器间通信而非进程间通信的并发系统。

这些决策也让 Go 语言在工具方面占尽优势。编译器是专为 Go 打造的，这意味着它能一门心思地快速编译 Go 代码。调试器能够理解 goroutine 以及 Go 的所有内置类型。

## 那么 Go 语言很棒吗？

这就见仁见智了。就我个人而言，我挺喜欢它的。我经手过的 Go 代码，读起来、理解起来通常都不费劲。它没有那些花里胡哨的东西，逼着我一门心思写实在的代码，而不是构建些华而不实的抽象概念。我还成功地向一大帮刚从大学毕业的新人传授过 Go 语言。

但这并不意味着我对它的缺点视而不见。有一回，我跟一位客户通电话，他碰上一个错误，就因为没检查错误，我们费了好大劲才追踪到问题所在。要是开着 `Linter`，这事儿本可轻松避免，可要是没开，那就麻烦了。Go 语言长久以来都不支持泛型，编写泛型数据结构的时候可费劲了。每次收到一份关于 Windows 系统的错误报告，我都得停下来琢磨琢磨，是不是 Go 语言让我产生了一种错误的安全感？

说到底，这些问题都是设计过程中有意权衡取舍的结果。你可以说不喜欢 Go 语言，或者它不适合某个应用场景，又或者它满足不了你的需求。甚至，你大可以直言讨厌它。但千万别断言它设计得糟糕。

---

- [本文长期连接](https://huizhou92.com/e8-af-91-e4-ba-8b-e5-ae-9e-e4-b8-8ago-e6-98-af-e4-b8-80-e7-a7-8d-e8-ae-be-e8-ae-a1-e8-89-af-e5-a5-bd-e7-9a-84-e8-af-ad-e8-a8-80/)
- 如果您觉得我的博客对你有帮助，请通过 [RSS](https://huizhou92.com/sitemap.xml)订阅我。
- 或者在[X](https://x.com/@piaopiaopig?ref=huizhou92.com)上关注我。
- 如果您有[Medium](https://medium.huizhou92.com/?ref=huizhou92.com)账号，能给我个关注嘛？我的文章第一时间都会发布在Medium。