安全优先 — Zig 为什么存在¶
这一章讲一件事: 已经有一打了系统级语言,Zig 凭什么还要出现、又凭什么值得你学。 作者的答案是一条推理链:主流语言各有三种老毛病 → 这三种病根源相同 (该透明的都不透明)→ Zig 把「透明」立为设计核心 → 安全、性能、可控成了同一件事的三个副产品。 这一章是全书的价值地基;后面十二章,每一章都在这条链上往下接一步。
1. 这一章讲什么¶
全书正文分三部分。第一部分(第 1–7 章)搭地基:装好环境、写出第一个程序, 再把控制流、函数、错误处理、测试这几样基本功练完。这一章是那一切之前的「为什么」, 正文几乎不含可运行的代码——它交给你的是一组判断标准: 以后每看到 Zig 的一个设计(第 3 章的变量、第 6 章的错误、第 9 章的内存), 你都可以拿同一句话去问——这件事在代码里看得见吗?
先交代作者立场,免得误读:这本书从头到尾假定读者愿意站在「底层」这一头—— 愿意自己管内存、想知道盖子底下发生了什么1。所以「系统级编程好不好」这个问题, 在书里从来不是「高层/底层选哪头」,而是「底层这一头为什么做得这么糟,能不能治好」。
2. 顶层全景:五步推理链¶
① 现象:系统级编程卡在两难里——高层语言会突然卡顿,底层语言到处是暗坑
│
② 归类:主流语言的三种具体病——垃圾回收 / 隐藏分配 / 构建复杂
│
③ 寻根:三种病的共同根源——语言在背后替你做了决定,而你从代码上看不出来
│
④ 处方:Zig 取消一切「背后的决定」——没有 GC、分配必须显式、build.zig 就是 Zig 代码
│
⑤ 收获:「透明」让错误藏不住、内存去向看得见、编译期还能提前验算——
「安全」因此被重新定义:代码做的就是你读代码时以为它做的事
图说:①→② 是现象归类,②→③ 是找根源,③→④ 是开处方,④→⑤ 是副作用清单。
这一章的主线例子在 §4.2:同一份「忘了初始化字段」的小程序, 在 C 里和在 Zig 里各走一遍命运——它是「透明」这一章主题的实体化身, 本章每个核心机制都能在它身上指出对应的一步。
3. 核心原理一:系统级编程的两难¶
3.1 先解释词:什么是系统级编程¶
系统级编程(systems programming)指贴着硬件干活的那类软件开发: 自己管内存、关心字节和时钟周期、处理高级语言替你藏起来的细节。 操作系统、驱动、嵌入式固件、游戏引擎、数据库、网络工具都在此列; 书里的定义是「软件开发中更贴近计算机硬件的那部分」2。
和它对举的是应用级编程:写网站、写 App、写脚本—— 活交给带垃圾回收的语言,内存有人替你收拾,你专注业务逻辑。
这两类的分界不在「难不难」,在谁对内存负责:
| 系统级(自己管) | 应用级(语言替你管) | |
|---|---|---|
| 内存的释放 | 你写代码明确释放,时机你定 | 垃圾回收器定期自动收 |
| 大小的关心 | 一个字节、一次时钟周期都要计较 | 够用就行,慢了就加机器 |
| 典型的错 | 崩溃,且常常查不出原因 | 卡顿,且常常不知何时来 |
3.2 两难本身¶
作者说,这个领域几十年卡在同一个两难里,两头都不满意3:
高层这头的好处与代价。 带垃圾回收的语言写得快:内存申请有人给、释放有人扫, 你几乎不用想这件事。代价是:运行时会毫无预兆地停顿一下—— 行话叫 stop-the-world(全世界停转):垃圾回收器要盘点哪些内存没人用了, 必须把程序整个冻住,数完再放行4。对一个游戏、一个交易程序、一个实时控制回路, 几十到几百毫秒的卡顿不是体验问题,是事故。书里给这类语言的判词是: 「简单,直到它性能不行了。」5
底层这头的好处与代价。 C 和 C++ 跑得快、每个字节都听你的, 但一个指针用错就崩溃——更糟的是,这类错常常是未定义行为: 程序不一定当场死,它可能「看起来好着」,然后在你最想不到的地方给出错误结果, 或者一周后在别人机器上崩。排这种错是这个行业里最贵的工时之一。 书里给这类语言的判词是:「强大,但危险。」6
一个要当场解释的术语:未定义行为(undefined behavior)。 意思是:语言标准对这种情况没有任何规定——编译器爱怎么处理怎么处理。 今天能跑,换个编译器版本、换台机器、换个优化档位,就可能变成另一副样子。 C/C++ 里的越界读写、用已经释放的内存、用没初始化的值,全是未定义行为。 「 undefined behavior 」这个英文词也记住,你在别人的文章和编译器警告里会再见到。
判断(我们的,不是书里的): 这两头的代价其实不对称。卡顿是可预期的坏—— 你知道它会来,只是不知道何时;未定义行为是不可预期的坏——你连它会不会来都不知道。 工程上,不可预期的坏贵得多,因为你要为「也许没事」付永久的测试和怀疑成本。 Zig 的全部设计,可以理解为把这个不对称性当成主要敌人。 如果错,会错在: 对某些负载(内存大、延迟不敏感的后台服务),GC 的停顿用加机器就能糊过去, 未定义行为的排查成本却省不掉——所以「底层代价更高」并非处处成立。
3.3 作者的学习主张(一段方法论,半页就够)¶
书里插入了一段学习心理学:大脑靠「新异刺激」记东西,对例行公事自动屏蔽7。 所以这本书刻意用反问、测验、动手练习逼人动脑,行文也刻意贫嘴—— 贫嘴是它的教学法,不是文风毛病。我们的拆解把段子滤掉、只留机制和判断, 但你拿原书当教材用时,要知道那些「废话」是有任务的。
4. 核心原理二:主流语言的三种病¶
这一节逐个拆作者点名的三个老毛病。每个都按同四步走:什么现象 → 为什么发生 → Zig 怎么治 → 边界在哪。三种病治完,再回头看它们的共同根源(§5)。
4.1 病一:垃圾回收——好心的不速之客¶
现象。 用带垃圾回收的语言写的程序,会在你没想到的时刻突然卡住。 书里的比喻:垃圾回收(GC)像个不请自来、弄乱屋子、留你收拾的朋友; 它「出发点是好的」,但当你的应用因为一次不合时宜的回收而卡顿时,出发点好不管用8。
为什么会发生。 先解释 GC(garbage collector,垃圾回收器):
它是语言运行时自带的一个打扫进程。你只管申请内存(new),不用管释放;
运行时定期盘点「哪些内存已经没人用了」,收回来。难点在「盘点」这一步:
要准确知道此刻谁在用什么,最稳的办法是把程序暂停,从所有还在用的变量出发,
把能摸到的内存标出来,剩下的收走。暂停期间,你的程序对世界完全无响应——
这就是 stop-the-world 这个名字的字面意思。
Zig 怎么治。 根本没有 GC:语言里没有这个机制,运行时不会背着你扫内存, 内存的申请和释放全部写在明面上,时机由你定9。对游戏服务器、实时系统这类 「停顿即事故」的场景,这是立项级的理由。
边界。 没有 GC 意味着释放也由你负责。忘了释放,就是内存泄漏 (memory leak:程序越跑占内存越多,最后拖垮机器);释放之后还接着用, 就是释放后使用(use-after-free:崩溃和漏洞的经典来源)。 Zig 不消灭这两类错——它把它们从「暗处」挪到「明处」,让每一件内存的去向都看得见。 具体怎么管,是第 9 章的主线。
4.2 病二:隐藏分配——不知道什么时候来的账单¶
现象。 有些语言里,一句看起来人畜无害的代码,背后会悄悄去要一块内存。 你不知道它在要,就没法预测它什么时候失败、什么时候变慢,更没法在内存紧张的环境里做预算。 书里的比喻:把车钥匙交给不会开车的人——一时可能没事,迟早出事10。
解释词:堆(heap)。 程序的内存分两大块(第 9 章细讲):栈(stack) 是函数临时的、自动收拾的工作台;堆是可以按需申请的大仓库—— 「分配(allocate)」这个词,通常特指从堆里要内存。隐藏分配的「分配」,说的就是堆。
Zig 怎么治,两条。 第一,语言层面没有 new 这类暗中分配的语法,
书里把保证写成了可以引用的一句原话:
「如果你不明确请求一次堆分配,它就不会发生。」11。
第二,标准库里每一个需要堆内存的功能,都显式地收一个参数,类型叫 Allocator(分配器:替你向操作系统要内存、再按需分给你的那件工具)。 因为每个要内存的调用点都非得把分配器递进去,内存决策永远在你手上,不在语言里12。 对照之下,书里点名两个邻居:Go 的协程(goroutine,Go 里轻量的并发执行单位)加 GC, 让隐藏分配很常见,内存受限时性能不可预测13;Rust 好一点, 但标准库也常常假定「有一块堆可用」,做不到完全无堆运行14—— 而 Zig 可以,这对嵌入式(内存只有几十到几百 KB 的小芯片)是硬指标。
边界。 「显式」的代价是啰嗦:别的语言一行 new ArrayList 的事,
Zig 里申请、扩容、释放每一步都要你亲手把分配器递进去(第 10 章全是这种手感)。
作者认为这个代价该付——因为「每个分配点可见」正是预算和排障的前提。
你读完第 9、10 章,可以带着自己的手感回来重判这一笔。
4.3 病三:构建复杂——编译之前先渡劫¶
现象。 很多语言「从源码构建出可执行文件」这件事本身,就是一套独立的学问: 构建脚本、依赖管理、配置生成、外部工具链,层层嵌套、环环相克。 作者的原话:一个语言的构建复杂到「需要构建系统的博士学位」, 对多数只想把代码编出来的开发者等于自动出局15。
解释词:工具链。 把源代码变成可执行文件,中间要经过编译器、汇编器、链接器等 一串工具,合称工具链。传统上这套东西每种操作系统各配一套: Linux 用 make/cmake,Windows 用 MSVC,macOS 用 Xcode—— 跨平台项目要为每个平台各写一套构建配置,这就是「渡劫」的具体内容。
Zig 怎么治。 三件事打包成一个命令:装 Zig 拿到的是单个静态编译好的可执行文件,
下载即用,不拖家带口16;构建系统就是语言本身,你写一个叫 build.zig 的文件,
它是一段 Zig 程序——不是一种新的配置语言,而是你能读、能调试、能重构的代码17;
外加 zig cc/zig c++ 两个命令能直接当 C/C++ 编译器用,自带 C 标准库,
交叉编译(用一个平台编译出另一个平台上跑的程序)开箱即用18。
边界。 第 11 章会展示,这套「用 Zig 写构建脚本」的方案威力大、初看也绕; 作者承认学习曲线不温柔,但认为「绕得明白」胜过「黑箱刚好能跑」—— 这恰是同一章三种病的同一个处方。
4.4 本章主线例子:同一份代码,两种命运¶
主角: 一个两行的小结构体 + 一个忘了初始化其中一半字段的变量。 (结构体 struct:把几个相关的值捆成一件东西的复合类型;字段 field:结构体里每个成员的名字。) C 和 Zig 都源自同一家族,语法几乎一样,正好做对照。代码是为演示写的,两版都是。
C 版(伪码,语法细节不追究):
struct Point { int x; int y; }; // 一个点:x 和 y 两个整数
struct Point p; // 声明一个点 —— 注意:没有给 y 赋值
p.x = 3; // 只初始化了 x
printf("%d", p.x + p.y); // 用到了没初始化的 y
Zig 版:
const Point = struct { x: i32, y: i32 };
var p: Point = undefined; // undefined:告诉 Zig「先别管里面是什么」
p.x = 3; // 同样只赋值了 x
std.debug.print("{d}", .{p.x + p.y}); // 同样用到了 y
一步步走:
| 步骤 | C 版的遭遇 | Zig 版的遭遇 |
|---|---|---|
| ① 编译 | 编译通过,编译器一声不吭 | 编译报错,信息大意:你声明了 p,但 y 从未被赋值19 |
| ② 运行前 | 你不知道有问题 | 程序根本生不出来,不进入下一步 |
| ③ 运行时(仅 C) | p.y 里是栈上恰好留在那的旧数据——上次哪个函数用过这块地方,就是什么数 |
(不存在这一步) |
| ④ 结果(仅 C) | 打印出一个每次可能都不同的数;运气差的话,这步越界读到不该读的内存,程序 later 崩在完全不相干的地方 | (不存在这一步) |
| ⑤ 修复成本 | 调试器、打印、猜测,工时从一小时到一周不等 | 把报错指的那行补上 p.y = ...,重新编译,完 |
这个例子的每一步对应本章的哪个机制,指得出来:
- 第 ① 行的差别 = 透明原则(§5):Zig 在编译期就把「背后发生的事」查了一遍, C 选择「背后的事不归我管」——这正是两种语言对「语言该替你兜多少底」的不同回答;
- 这个错的性质 = 未定义行为(§3.2):C 标准对「读到没初始化的值」没有规定, 所以同一个程序今天能跑、明天能崩;
- 报错的时机 = 安全观(§7):把「运行时才炸」的错误挪到「编译时拒」;
- 若这个错不是漏初始化、而是越界读写——那就是另一张安全网(数组下标检查)管的, 见 §7 的表。
判断(我们的,不是书里的): 注意这个例子的不对称:Zig 赢的不是「更聪明」, 而是「更早」——把同一件事从运行时挪到了编译时。整章所有安全设计都是这一个动作的重复: 能编译期拒的,绝不拖到运行;非运行时查不可的(比如越界),让你按构建模式自己选要不要这笔检查费。 记住「把错误往早挪」这五个字,第 6 章的错误处理、第 7 章的测试、第 9 章的内存, 全是它的变奏。 如果错,会错在: 编译期能抓的错误种类是有限的——输入错、逻辑错、环境错, 该运行时炸的还是得炸。「更早」不等于「全包」。
5. 核心原理三:三种病的共同根源——不透明¶
上面三种病,药方各不相同;但作者真正想让你看见的是同一件事:
垃圾回收 → 内存在你不知情时被搬动、被回收 ┐
隐藏分配 → 内存在你不知情时被请求 ├ 语言/工具在背后替你做了决定,
复杂构建 → 编译过程被一堆脚本在暗中决定 ┘ 而代码里看不出来
│ 共同根源
▼
Zig 的处方:取消一切「背后的决定」——看到什么就是什么
这个处方落到 Zig 的几个招牌设计上。下面每个,书里给的是观点句; 机制细节留给对应章节,但判断标准在这一章就立起来。
5.1 没有隐藏控制流¶
书里给了一段三行代码,让你猜「哪一行背后有坑」:
答案是:没有坑。 funcA(); funcB(); 就是先调 A 再调 B。
书里的原话列了三样 Zig 没有的东西20:
| Zig 没有的 | 它是什么 | 在别的语言里长什么样 |
|---|---|---|
| 暗地触发的函数调用 | 读起来是普通操作、实际会跳进别的代码 | C++ 的隐式转换、某些语言的 getter 被自动调用 |
| 运算符重载(operator overloading) | +、< 这些符号可以被重定义成任何事 |
C++ 里 a + b 可能打开一个数据库 |
| 属性访问器(property accessor) | 读一个字段可能偷偷执行一段代码 | 写 obj.x 实际跑了一段函数,行为随实现变 |
这三样的共同点是:眼睛看到的执行顺序,不是真实的执行顺序。 而排障的第一课是「怀疑你读到的代码」——语言每藏一层,你要怀疑的层就多一层。 Zig 把这件事归零:读任何一段 Zig 代码,执行顺序就是你眼睛看到的顺序21。 书里的总结:「代码做的正是它说的。」22
判断(我们的,不是书里的): 「没有隐藏控制流」是这本书里最被低估的一条。 它和性能无关、和安全也隔着一层,但它决定了一门语言代码的可读成本曲线: 项目越大、人越多,「我能不能信我眼前这行字」就越值钱。 C++ 二十年前的设计债,大半是这三样特性叠出来的。 如果错,会错在: 这条的好处是「省了怀疑」,而怀疑有时能逼着人理解底层—— 一个从没被隐式转换坑过的初学者,可能对内存布局反而迟钝。算是代价,不算反例。
5.2 编译期执行(comptime)¶
comptime 是 compile time(编译期)的缩写:让一段代码在编译阶段就跑起来23。 跑在编译期的代码能做的事:预先算好值、检查类型、甚至生成代码—— 所有结果直接烤进最终程序,运行时零开销24。
解释词:编译期和运行时。 一段程序的一生分两截: 编译期是源代码被翻译成可执行文件的那段过程(你敲下编译命令到生成文件); 运行时是你双击可执行文件、程序真正干活的那段过程。 普通语言的代码全都生在运行时;comptime 让一部分代码提前到编译期生。
它撑起了 Zig 的两样东西:
- 泛型(generics): 写一份对任何类型都成立的代码。别的语言靠专门的泛型机制或宏, Zig 靠「在编译期把具体类型代进去跑一遍」——comptime 是它的地基;
- 编译期元编程: 在编译期做计算、做校验、生成代码。 书里的比方:给编译器一个水晶球,让它在程序开跑前就预见并准备好一切25; 又拿宏(macro,其他语言里在编译前做文本替换的预处理机制)做对照: 宏像胶带纸——能粘,但不透明、易错、难调试;comptime 干净、类型安全26。
判断标准由此又多一条:凡是「每次运行都要重复做的同一件验算」, 都该问一句——它能不能挪到编译期做掉? 第 12 章专讲这个,这里只立名字。
5.3 工具链一体化¶
一个 zig 命令同时是:编译器、构建系统、C/C++ 编译器(zig cc、zig c++)、
交叉编译器,还自带测试运行器(zig test)27。
和 Go 的工具链对比:Go 也以「开箱即用」出名,Zig 走得更远的点在于
交叉编译和直接 C/C++ 互操作这两件更硬的场景28。
这一条的真正落点在下一节——它是「Maintain with Zig」的硬件基础。
6. 核心原理四:Maintain with Zig —— 全书的方法论枢纽¶
这一节讲本章最长的一条推理,也是这本书区别于「又一本语言入门」的地方: Zig 的野心不是替代 C,而是接管 C 项目的后半生。
6.1 从 Kotlin 和 Carbon 借来的论证¶
作者先举了两个「渐进采用」的先例,帮你把这个策略看清楚。
Kotlin: JetBrains 公司开发的静态类型语言,跑在 Java 虚拟机(JVM)上。 它的核心卖点是能和 Java 代码逐文件混用:老项目一行不用重写, 可以一个文件一个文件地换成 Kotlin,每换一个文件就立即享受新语言的特性 (空值安全、更简洁的语法、协程)29。
Carbon: Google 发起的「C++ 继任者」实验语言。设计目标同样不是推翻重来, 而是和现有 C++ 代码手拉手演进:Carbon 代码可以调用 C++,逐步替换30。
两家的共同策略,书里叫渐进采用(progressive adoption):不重写,只替换; 不否定存量,只改善增量。作者说 Zig 走同一条路,而且工具更硬——因为 Zig 能直接编译 C。
6.2 关键事实:Rust 不编译 C¶
书里最尖的一句俏皮话,恰恰是全书最硬的一个事实:
「Rust 不构建 C。」31
拆开看这句话。要把一段老 C 代码「迁到 Rust」,你的唯一办法是手工翻译:
人肉读 C、人肉写 Rust,过程中丢掉原代码的每一次编译器特性和平台适配,
还得祈祷没译错。而 Zig 自带 libc(C 的标准函数库,每个操作系统都有的那套底层接口)
和 zig cc、zig c++ 两个命令,可以直接当 C 编译器用,还附带交叉编译能力32。
所以对存量 C/C++ 项目,Zig 的角色不是「替代者」而是「管家」:
存量 C/C++ 项目 ──→ 构建脚本换成 build.zig(zig cc 编译老代码)
│
├─ 老代码:继续用、继续维护,一行不用翻
└─ 新模块:用 Zig 写,和老代码互相调用
图说:同一个项目里两种语言共存,增量替换,永不重写。
书里的断言:用一个 build.zig 文件,不用 Xcode、不用 MSVC、不用 cmake/make,
就能把你的项目编译到所有平台33。这就是口号 Maintain with Zig(用 Zig 维护)的实体。
作者还专门花一段反驳「那为什么不干脆用 Rust 重写」:
重写会丢掉老代码里攒了多年的 bug 修复和真实世界适配,
让对手赢得时间;重构、优化、重组式的渐进改良才是正路34。
判断(我们的,不是书里的): 「Rust 不构建 C」是真事实,但常被略过一个前提: 你手里得确实有一堆值得保留的 C 代码。对新项目,这条论证不成立——新项目选语言 比的是生态、库、招人。所以「Maintain with Zig」是 Zig 最锋利的一块使用场景说明, 而不是普适的推荐理由。书里没把这个边界画出来,我们替它画上。 如果错,会错在: 如果 Zig 的新项目生态在 1.0 前后出现爆发(库够多、岗位够多), 「主要为存量 C 项目而生」这个限定就过时了——这在成书后的 Zig 社区讨论里已是常见话题。
6.3 顺带的统一证明:cgo 难题¶
书里补了一个很具体的案例,展示「工具链一体化」能插进别家生态里起作用。 cgo 是 Go 语言调 C 代码的官方机制:Go 程序里写一段特殊注释, 就能调用 C 函数,用上现成的 C 库35。但 cgo 有个著名的痛点:交叉编译。 因为它要为目标平台单独准备 C 工具链,每加一个目标平台就多一套繁琐配置, 「给每个目标平台做繁琐配置」是书里对 cgo 的原话概括36。
而 Go 的工具链可以换成 Zig 来驱动。于是出现一个漂亮的局面: 用 Zig 当底层工具链,cgo 的跨平台配置难题「被无意中解决」—— 不再需要为每个目标平台单独折腾,因为 Zig 自己就把交叉编译管了37。 这一段的分量在于:它证明一体化工具链不是自卖自夸,而是能在别人的地盘上当零件用。
6.4 安全与速度的四个档位¶
透明带来的另一个副产品:安全检查做成了可按档位购买的选项, 而不是「要么全要要么全不要」。书里列出四种构建模式38:
| 模式 | 干什么用 | 优化 | 安全检查 |
|---|---|---|---|
| Debug | 日常开发、找 bug | 关 | 全开 |
| ReleaseSafe | 上线但要稳 | 开 | 保留关键检查 |
| ReleaseFast | 极限速度 | 开 | 大部分移除 |
| ReleaseSmall | 空间紧张的环境(嵌入式) | 为体积优化 | 大部分移除 |
一个越界访问的程序在四个档位下的命运(这是按机制推演的演示,原书没给这个走查):
Debug 档当场抓包报错;ReleaseSafe 档抓包、但程序本身跑得更快;
ReleaseFast 档不查、直接读越界内存(快,但可能崩或给出错结果);
ReleaseSmall 档同样不查,同时把可执行文件压到最小。
同一个程序、同一份代码,四档四种行为——「安全」在这里第一次可以按场景交易。
第 11 章讲怎么在 build.zig 里选档。
7. 核心原理五:安全观的重新定义——本章落点¶
这一节是本章的落点,值得单独看清。书里的关键句,作者甚至建议读者做成标语牌:
「内存安全只是软件正确性的一个支柱。」39
7.1 「安全」这个词被用滥了¶
作者说,最近几年「安全」是编程语言圈的第一热词,「Rust 更安全」这句话到处都能见到。 这话不假——但「安全」是个被塞了太多东西的行李箱40。 内存安全(memory safety)专指一件事:程序不会因为指针和内存的错误 (越界、释放后使用、空指针)而读出或写坏不相干的数据。Rust 的主战场就是这里, 它的所有权机制在编译期堵死这类错,这是真功夫。
但作者说,Zig 盯的是更大的东西——整体正确性(overall software correctness): 你的代码做的,是你以为它做的事41。内存安全只是这个大目标的一个支柱; 其他的支柱包括:每个字段都初始化了吗?每个错误都被处理了吗?每个下标都在界内吗? 这些没有一个属于「内存安全」的狭义定义,但任何一个塌了,软件都不正确。
用一张表看清两种「安全观」的口径差:
| 内存安全观 | 整体正确性观 | |
|---|---|---|
| 核心问题 | 内存会不会被用坏 | 代码做的和我想的是不是一回事 |
| 主要手段 | 所有权、借用检查(编译期约束指针用法) | 把每类错误挪到编译期拒或运行当场抓 |
| 盲区 | 逻辑错、漏初始化、被忽略的错误,都不在「内存安全」定义内 | 没有盲区定义,只有「还没覆盖的错误类别」 |
| Zig 的位置 | 不在此列——Zig 不用所有权 | 主战场 |
书里给 Zig 的角色白描:它像那个说话直截了当的叔叔,不在乎酷不酷,只在乎活干得对不对; 它的设计就是「防止你聪明反被聪明误」42。
7.2 落到具体:六张安全网¶
书的后半段把「整体正确性」拆成六张具体的安全网43。每张都注明机制在哪一章展开, 这里先把「它防什么」钉死:
| # | Zig 的设计 | 防的错误 | C 里的对应病 |
|---|---|---|---|
| 1 | 结构体的每个字段必须初始化,否则编译不过 | 用到没初始化的值(值是随机的旧数据,行为不可预测) | C 里忘初始化一半字段,编译器一声不吭——§4.4 主线例子演示的就是这张网 |
| 2 | 错误必须写进函数签名(第 6 章):一个函数可能失败,它的返回类型里必须明说 | 调用者假装错误不会发生,失败在远处爆炸 | C 里靠「返回 -1 表示出错」的口头约定,人人可以假装没看见 |
| 3 | 指针默认不允许是空(null)(第 4、9 章);想要可空,必须显式写成 ?T |
空指针崩溃——C 程序崩溃原因的头几名 | C 里任何指针都可能是空,检查靠自觉 |
| 4 | 数组下标越界会被当场抓住(Debug/ReleaseSafe 档) | 读写越过数组边界、踩坏旁边的内存 | C 里越界是未定义行为,常常「看起来好着」 |
| 5 | comptime 编译期验算(第 12 章) | 能在编译期发现的矛盾(类型不配、常量算错) | C 里这类错要到运行才冒头 |
| 6 | 内存布局可控(字段重排 = 编译器调换字段顺序、紧凑打包,第 8、12 章) | 对齐(数据起点要合 CPU 口味)错误、结构体里塞满空洞浪费内存 | C 里布局基本听编译器的,想对齐得用编译器专有扩展 |
这张表的正确读法是按两列分类:第 1、2、3、5 张网在编译期拒人——错误活不过编译; 第 4 张网在运行当场抓——程序确实慢一点点,但炸在出错的那一刻、而不是三天后的另一行; 第 6 张网给你控制手段——它防的不是崩溃,是性能和可移植性的暗坑。
判断(我们的,不是书里的): 这张表里最值得注意的是第 3 张网「指针默认非空」。 它成本几乎为零(只是类型系统里多一个问号记号),却消灭了 C 崩溃清单上的头部死因—— 这是「用类型系统买安全」的教科书案例,和 Rust 的所有权是同一个思路的轻量版: 不靠运行时检查,靠让错误在类型上表达不出来。 六张网里性价比最低的是第 6 张的「布局控制」:它救的是性能和移植性,救不了正确性, 初学者完全可以晚几年再碰。 如果错,会错在: 「编译期拒人」的前提是编译器看得见—— 从文件读进来的数据、网络收到的数据,类型再严也拦不住内容错, 这类错的防线是第 6 章的错误处理和第 7 章的测试,不在本章这张表里。
7.3 「现在就能用」与「还没 1.0」并读¶
作者反复强调:Zig 不是画饼,每个承诺此刻就能兑现——「不是高远的愿景, 而是已经上线、能用于真实项目的实用方案」44。 但同一章的后半段,作者也明说:Zig 还没到 1.0,会有定期的破坏性变更, 生产环境采用要「拉住缰绳」45。
这两句必须放在一起读,口径才是完整的:机制是真的,稳定性是过渡期的。 写作时(2025 年)最新稳定版是 0.15.x46;今天读这本书, 所有具体语法、标准库函数名都要带一个心眼——可能对不上了,但第 0 章讲的这套「为什么」, 一个字都不会过期。
8. 作者的判断与证据¶
这一章里「可核的事实」和「作者的立场」必须分开标。
书里给的事实类(可核):
- Zig 没有 GC、没有隐藏分配;标准库里需要堆的功能显式收 Allocator 参数1112;
zig cc/zig c++存在,可直接当 C 编译器用;自带 libc;能交叉编译;还在做自己的链接器 zld 以进一步摆脱 LLVM32;- 四种 release 模式存在且行为如 §6.4 所述38;
- 六张安全网存在:未初始化字段编译报错、错误进签名、指针默认非空、越界运行检查、comptime、布局控制——各章会逐个展开。
作者的立场类(判断,不是证明):
- 「三种病的共同根源是不透明」——这是作者的归因,一种有说服力的读法,不是测量出来的结论;
- 「重写老项目几乎总是战略失误」——书里给的是论证(丢 bug 修复、贻误战机),不是数据34;
- 「Zig 此刻就能兑现承诺」——作者在书中反复断言44,但同章承认 1.0 未至45,两句并读才完整;
- 「简单、透明、可移植的黄金平衡点被 Zig 占据」——这是全章的价值判断,作者没有、也给不出测量47。
证据的成色,照实说: 本章通篇没有一张基准测试图、没有一个对照实验, 「更快」「更安全」全靠机制和逻辑论证支撑。对一本导论书这够格; 但你要拿它去说服别人时,引机制,别引形容词。
9. 边界与局限¶
照实交代这一章没覆盖、或已被时间冲刷的:
| 缺什么 | 说明 |
|---|---|
| 怎么写 Zig 代码 | 这一章几乎没有可运行的内容。pub fn main 那几行是预告片,正文从第 3 章才开始48 |
| 对「透明」的成本核算 | 没有 GC、没有隐藏分配的另一面是:每个初学者迟早写出自己的第一个内存泄漏。本章把「控制」写成纯收益,代价要到第 9 章才现身 |
| 1.0 之前的版本风险 | 书里明说:写作时 Zig 最新稳定版是 0.15.x,语言仍在演进,示例代码可能随版本失效46。读这本书的所有代码都要带这个前提 |
| 同行语言的刻画有倾向 | C++「只会堆特性」、Go「隐藏分配多」这类说法服务于论证,不是中立的生态调查;拿它当「Zig 视角」读,别当行业共识 |
| 性能数字为零 | 本章所有「快」「高效」都是定性的,没有一个基准测试数字支撑 |
| Kotlin/Carbon 的画像偏简 | 两家各给一小段,够支撑「渐进采用」的类比,不够支撑对这两门语言的任何具体判断 |
| 「Zig 社区」只是一笔带过 | 这一章末尾点了社区和未来的名字(书里指第 14 章),但内容为零;真正接住这个话题的是原书第 13 章的社区资源一节 |
10. 可带走的¶
本章主线例子,一行走完: 同一份「忘了初始化字段」的代码, C 编译通过、运行时读到随机旧数据;Zig 编译直接报错、逼你当场补上—— 「安全优先」就是把这类错误从「运行时炸」挪到「编译时拒」,支撑它的总原则是「没有隐藏行为」。
- 系统级编程的两难: 高层语言会突然卡顿(stop-the-world),底层语言到处是未定义行为的暗坑;Zig 的赌注是治好底层这头的暗坑,而不是换一头;
- 三个老毛病: 垃圾回收(不定期卡顿)、隐藏分配(不知何时要内存)、构建复杂(跨平台配置渡劫);对策分别是:没有 GC、分配必须显式传 Allocator、
build.zig就是 Zig 代码; - 共同根源是「不透明」: 语言在背后替你做了决定——Zig 的处方是「看到什么就是什么」;
- 没有隐藏控制流: 代码的执行顺序就是你读到的顺序;没有运算符重载、没有暗地触发的调用、没有属性访问器;
- comptime(编译期执行): 让代码在编译阶段先跑一遍——预计算、验类型、生成代码、撑起泛型,运行时零开销;
- 「内存安全只是软件正确性的一个支柱」: Zig 追求更大的整体正确性,六张安全网(强制初始化、错误进签名、指针默认非空、越界检查、comptime、布局控制)是它的实体;
- 「把错误往早挪」五字诀: 能编译期拒的绝不拖运行;非运行查不可的,按构建模式自选要不要检查费;
- Maintain with Zig:
zig cc直接编译 C,所以对存量 C/C++ 项目的正确姿势是「混搭渐进」而非重写——「Rust 不构建 C」是这条的支点,但它只对有 C 存量的项目成立; - 安全检查分四档: Debug / ReleaseSafe / ReleaseFast / ReleaseSmall,同一程序四档四种行为;
- 写于 Zig 0.15 时代、未到 1.0: 机制讲解保值,具体语法和 API 可能已变;本章所有对比都是「Zig 视角」,是论证不是调查报告。
11. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 系统级编程的定义与两难 | Chapter 1: Safety First | text/05-fm-preface.txt:3(搜「much closer to the computer」) · text/05-fm-preface.txt:18(搜「Simple, until」) |
| 大脑靠新异刺激学习、全书教学法 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:90(搜「Your brain thrives on」) |
| Kelley 的动机与「我们可以做得更好」 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:144(搜「Fed up with the endless landmines」) · text/07-ch01-chapter-1-safety-first.txt:151(搜「we can do better」) |
| 垃圾回收的 stop-the-world | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:177(搜「stop-the-world latency glitches」) |
| 「不明确请求堆分配就不会发生」 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:216(搜「explicitly request a heap」) |
| Go 的隐藏分配、Rust 的标准库假定堆 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:219(搜「hidden allocations are common」) · text/07-ch01-chapter-1-safety-first.txt:223(搜「assumes the presence of a heap」) |
| 标准库显式收 Allocator | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:227(搜「using an Allocator parameter」) |
| 构建复杂是「博士学位」 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:196(搜「PhD in build systems」) |
| 单文件工具链 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:318(搜「compiled binary that includes everything」) |
| 没有隐藏控制流的三行代码 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:282(搜「var r = x + y.z」) · text/07-ch01-chapter-1-safety-first.txt:288(搜「operator overloading」) · text/07-ch01-chapter-1-safety-first.txt:290(搜「nothing to hide」) |
| comptime 与泛型、元编程、宏对照 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:300(搜「short for compile-time」) · text/07-ch01-chapter-1-safety-first.txt:303(搜「crystal ball」) · text/07-ch01-chapter-1-safety-first.txt:11(搜「compile-time metaprogramming」) |
| Kotlin / Carbon 的渐进采用 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:421(搜「progressive interoperability」) · text/07-ch01-chapter-1-safety-first.txt:412(搜「one file at a time」) · text/07-ch01-chapter-1-safety-first.txt:417(搜「incrementally」) |
| 「Rust doesn't build C」 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:510(搜「doesn’t build C」) |
| zig cc / zig c++、自带 libc、zld | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:518(搜「zig cc and zig c++」) · text/07-ch01-chapter-1-safety-first.txt:520(搜「its own linker」) |
| 四种 release 模式 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:545(搜「Debug: Perfect for development」) |
| 「内存安全只是一个支柱」 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:600(搜「pillar of software correctness」) |
| 字段必须初始化 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:605(搜「forget to initialize」) |
| 指针默认非空 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:621(搜「non-null pointers by default」) |
| 下标越界检查 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:625(搜「safe indexing」) |
| 适用领域清单 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:649(搜「Embedded systems」) |
| 未到 1.0、会有破坏性变更 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:667(搜「regular refactors and」) |
| 「现在就能兑现,不是画饼」 | Chapter 1: Safety First | text/07-ch01-chapter-1-safety-first.txt:634(搜「anything but a pipe dream」) |
| 第一部分地图 | Part 1: Zig Fundamentals | text/06-ch01-part-1-zig-fundamentals.txt:3(搜「lays the groundwork」) |
| 目标读者画像 | Preface | text/05-fm-preface.txt:58(搜「programming professionally for」) |
| 写作时的 Zig 版本(0.15) | Preface | text/05-fm-preface.txt:139(搜「version 0.15」) |
-
出处:「Preface」第 58–63 段(
text/05-fm-preface.txt:58,搜「programming professionally for」)。原书明确写给「有两三年专业编程经验」的开发者:不教零基础写代码,假定你已会变量、函数、循环;不要求系统级或 C/C++ 背景,但要求「愿意和新的、有时很难的概念死磕」。 ↩ -
出处:「Preface」第 2–5 段(
text/05-fm-preface.txt:3,搜「much closer to the computer hardware」)。原文:系统级编程是「更贴近计算机硬件」的那部分软件开发,要自己理解底层、小心管理内存、处理被易用工具藏起来的细节。 ↩ -
出处:「Preface」第 14–23 段(
text/05-fm-preface.txt:16,搜「High-level ease」;text/05-fm-preface.txt:19,搜「Directly manage memory and resources」)。两难的三元表述(高层易用/底层控制/无法三全)是原文结构。 ↩ -
出处:「Chapter 1: Safety First」第 174–180 段(
text/07-ch01-chapter-1-safety-first.txt:177,搜「stop-the-world latency glitches」)。原文把垃圾回收比作不请自来、弄乱屋子、留你收拾的朋友。 ↩ -
出处:「Preface」第 18 段(
text/05-fm-preface.txt:18,搜「Simple, until it」)。原文:「Simple, until it's not performing well.」 ↩ -
出处:「Preface」第 21 段(
text/05-fm-preface.txt:21,搜「Powerful, but risky」)。原文:「Powerful, but risky.」 ↩ -
出处:「Chapter 1: Safety First」第 90–94 段(
text/07-ch01-chapter-1-safety-first.txt:90,搜「Your brain thrives on」)。原文:大脑对新异刺激反应强烈、对例行公事自动屏蔽,所以全书用反问、测验和练习逼人动脑。 ↩ -
出处:「Chapter 1: Safety First」第 172–180 段(
text/07-ch01-chapter-1-safety-first.txt:174,搜「sounds like a developer」)。「纸上是梦、现实是添乱的朋友」的比喻见第 175–178 段。 ↩ -
出处:「Chapter 1: Safety First」第 202–209 段(
text/07-ch01-chapter-1-safety-first.txt:204,搜「skips the garbage collector entirely」)。「没有 GC、给你内存管理的完全控制权」是原文的明确表述。 ↩ -
出处:「Chapter 1: Safety First」第 184–189 段(
text/07-ch01-chapter-1-safety-first.txt:187,搜「handing the keys to your car」)。原文:把内存分配的决定权交给系统,像把车钥匙交给不会开车的人。 ↩ -
出处:「Chapter 1: Safety First」第 211–217 段(
text/07-ch01-chapter-1-safety-first.txt:216,搜「explicitly request a heap」)。原文原话:「If you don't explicitly request a heap allocation, it won't happen.」 ↩↩ -
出处:「Chapter 1: Safety First」第 227–228 段(
text/07-ch01-chapter-1-safety-first.txt:227,搜「using an Allocator parameter」)。原文:「Every standard library feature that requires heap memory is explicit.」 ↩↩ -
出处:「Chapter 1: Safety First」第 219–221 段(
text/07-ch01-chapter-1-safety-first.txt:219,搜「hidden allocations are common」)。原文点名 Go:协程和 GC 的自动管理让隐藏分配常见,导致内存受限环境下性能不可预测。 ↩ -
出处:「Chapter 1: Safety First」第 223–225 段(
text/07-ch01-chapter-1-safety-first.txt:223,搜「assumes the presence of a heap」)。原文同时说 Zig 可以完全不用堆运行,适合嵌入式和实时应用。 ↩ -
出处:「Chapter 1: Safety First」第 191–197 段(
text/07-ch01-chapter-1-safety-first.txt:196,搜「PhD in build systems」)。原文:构建过程复杂到要「构建系统博士学位」的语言,对多数开发者等于自动出局。 ↩ -
出处:「Chapter 1: Safety First」第 317–318 段(
text/07-ch01-chapter-1-safety-first.txt:318,搜「compiled binary that includes everything」)。原文:「a single, statically compiled binary that includes everything you need—just download and start building」。 ↩ -
出处:「Chapter 1: Safety First」第 430–445 段(
text/07-ch01-chapter-1-safety-first.txt:431,搜「designed to replace traditional build systems」;text/07-ch01-chapter-1-safety-first.txt:447,搜「basic example」)。build.zig示例见同章该段起的代码块。 ↩ -
出处:「Chapter 1: Safety First」第 370–383 段(
text/07-ch01-chapter-1-safety-first.txt:377,搜「same build.zig file」)与第 516–521 段(text/07-ch01-chapter-1-safety-first.txt:518,搜「zig cc and zig c++」)。 ↩ -
出处:「Chapter 1: Safety First」第 604–609 段(
text/07-ch01-chapter-1-safety-first.txt:605,搜「forget to initialize」)。原文:C 里可以建一个结构体然后忘掉初始化一半字段,这是滴答作响的定时炸弹;Zig 里每个字段都必须初始化,否则编译器打你。报错行为是 Zig 未初始化检查的通行语义,各章有代码演示。 ↩ -
出处:「Chapter 1: Safety First」第 282–290 段(
text/07-ch01-chapter-1-safety-first.txt:288,搜「operator overloading」)。原文明确列出没有的三样:sneaky hidden function calls、unexpected surprises from operator overloading、property accessors。 ↩ -
出处:「Chapter 1: Safety First」第 287–290 段(
text/07-ch01-chapter-1-safety-first.txt:287,搜「funcA() followed by funcB()」)。原文:「When you see funcA() followed by funcB(), that's precisely what happens」。 ↩ -
出处:「Chapter 1: Safety First」第 290 段(
text/07-ch01-chapter-1-safety-first.txt:290,搜「nothing to hide」)。原书小节结尾:「Zig's got nothing to hide!」 ↩ -
出处:「Chapter 1: Safety First」第 299–303 段(
text/07-ch01-chapter-1-safety-first.txt:300,搜「short for compile-time」;text/07-ch01-chapter-1-safety-first.txt:302,搜「generate and manipulate code」)。 ↩ -
出处:「Chapter 1: Safety First」第 299–310 段(
text/07-ch01-chapter-1-safety-first.txt:310,搜「runtime overhead」)。 ↩ -
出处:「Chapter 1: Safety First」第 303 段(
text/07-ch01-chapter-1-safety-first.txt:303,搜「crystal ball」)。 ↩ -
出处:「Chapter 1: Safety First」第 264–271 段(
text/07-ch01-chapter-1-safety-first.txt:265,搜「no macros」)与第 45–47 段关于宏的「胶带纸」对照见「Chapter 12: Sophisticated Topics」的 comptime 一节(text/20-ch12-chapter-12-sophisticated-topics.txt:45,搜「duct tape」)。 ↩ -
出处:「Chapter 1: Safety First」第 316–335 段(
text/07-ch01-chapter-1-safety-first.txt:317,搜「hassle-free as possible」;text/07-ch01-chapter-1-safety-first.txt:333,搜「zig test」)。 ↩ -
出处:「Chapter 1: Safety First」第 324–329 段(
text/07-ch01-chapter-1-safety-first.txt:329,搜「distinct edge」)。原文:Go 的工具链简洁快速,Zig 在复杂构建场景——尤其交叉编译与 C/C++ 互操作——更进一步。 ↩ -
出处:「Chapter 1: Safety First」第 400–413 段(
text/07-ch01-chapter-1-safety-first.txt:408,搜「seamlessly integrates with existing Java」;text/07-ch01-chapter-1-safety-first.txt:412,搜「one file at a time」)。 ↩ -
出处:「Chapter 1: Safety First」第 404–406 段(
text/07-ch01-chapter-1-safety-first.txt:404,搜「successor to C++」)与第 415–419 段(text/07-ch01-chapter-1-safety-first.txt:417,搜「incrementally」)。 ↩ -
出处:「Chapter 1: Safety First」第 510 段(
text/07-ch01-chapter-1-safety-first.txt:510,搜「doesn’t build C」)。原文:「Rust doesn't build C. So, rewriting is not a very progressive approach, right? RIGHT?」 ↩ -
出处:「Chapter 1: Safety First」第 517–521 段(
text/07-ch01-chapter-1-safety-first.txt:518,搜「zig cc and zig c++」)。原文:Zig 自带一组 libc 以便交叉编译,zig cc/zig c++是 Clang 的就地替代品;项目还在做自己的链接器 zld 以替代 lld、降低对 LLVM 的依赖。 ↩↩ -
出处:「Chapter 1: Safety First」第 439–445 段(
text/07-ch01-chapter-1-safety-first.txt:440,搜「Xcode, MSVC」)。 ↩ -
出处:「Chapter 1: Safety First」第 483–507 段(
text/07-ch01-chapter-1-safety-first.txt:493,搜「bug fixes, optimizations」;text/07-ch01-chapter-1-safety-first.txt:498,搜「incremental improvements」)。重写会丢掉多年攒下的 bug 修复与适配,作者主张重构、优化、重组式的渐进改良。 ↩↩ -
出处:「Chapter 1: Safety First」第 533–538 段(
text/07-ch01-chapter-1-safety-first.txt:535,搜「allows Go programs to call C code」)。 ↩ -
出处:「Chapter 1: Safety First」第 533–538 段(
text/07-ch01-chapter-1-safety-first.txt:529,搜「intricate configurations」)。 ↩ -
出处:「Chapter 1: Safety First」第 527–531 段(
text/07-ch01-chapter-1-safety-first.txt:528,搜「unintentionally solves cgo issues」)。 ↩ -
出处:「Chapter 1: Safety First」第 543–552 段(
text/07-ch01-chapter-1-safety-first.txt:545,搜「Debug: Perfect for development」)。四种模式的名称与定位照原书列表;ReleaseFast「removes most safety checks」、ReleaseSmall「smallest possible binary size」见第 549–552 段。 ↩↩ -
出处:「Chapter 1: Safety First」第 599–600 段(
text/07-ch01-chapter-1-safety-first.txt:600,搜「pillar of software correctness」)。原书让读者把这句话做成标语牌放在手边。 ↩ -
出处:「Chapter 1: Safety First」第 583–587 段(
text/07-ch01-chapter-1-safety-first.txt:583,搜「ultimate buzzword」;text/07-ch01-chapter-1-safety-first.txt:587,搜「thrown around like confetti」)。 ↩ -
出处:「Chapter 1: Safety First」第 589–598 段(
text/07-ch01-chapter-1-safety-first.txt:591,搜「overall software correctness」)。原文:Zig 关注的是整体软件正确性,「no smoke and mirrors」。 ↩ -
出处:「Chapter 1: Safety First」第 590–594 段(
text/07-ch01-chapter-1-safety-first.txt:590,搜「straight-talking uncle」;text/07-ch01-chapter-1-safety-first.txt:593,搜「outsmarting yourself」)。 ↩ -
出处:「Chapter 1: Safety First」第 604–630 段(
text/07-ch01-chapter-1-safety-first.txt:605,搜「forget to initialize」;:611,搜「practically forces you」;:621,搜「non-null pointers by default」;:625,搜「safe indexing」;:629,搜「footguns」)。六张网是对该段的拆解与重组;布局控制(field reordering and packing)见第 619–620 段。 ↩ -
出处:「Chapter 1: Safety First」第 632–638 段(
text/07-ch01-chapter-1-safety-first.txt:634,搜「anything but a pipe dream」;text/07-ch01-chapter-1-safety-first.txt:635,搜「operational right now」)。 ↩↩ -
出处:「Chapter 1: Safety First」第 666–673 段(
text/07-ch01-chapter-1-safety-first.txt:667,搜「regular refactors and」;:671,搜「1.0 release」)。 ↩↩ -
出处:「Preface」第 136–154 段(
text/05-fm-preface.txt:139,搜「version 0.15」)。写作时最新稳定版为 0.15,第 2 章注明 0.15.2(text/08-ch02-chapter-2-setting-up-your-playground.txt:39,搜「0.15.2」)。 ↩↩ -
出处:「Chapter 1: Safety First」第 129–132 段(
text/07-ch01-chapter-1-safety-first.txt:132,搜「plants its flag」)。「占据简雅可移植的甜蜜点」是作者的价值断言,原文为 Zig「plants its flag there and dares anyone to do better」。 ↩ -
出处:「Chapter 1: Safety First」第 249–259 段(
text/07-ch01-chapter-1-safety-first.txt:251,搜「const std = @import」)。书中预告式的第一段 Zig 代码,并明说「别急着搞懂每一部分」(text/07-ch01-chapter-1-safety-first.txt:244,搜「sneak peek」)。 ↩