编程语言研究书库

第 2 章拆解 — 构建、测试与发布:把 Zig 项目当成基础设施

构建、测试与发布:把 Zig 项目当成基础设施

这一章讲什么: 构建系统通常是系统编程书里最干的部分——Makefile 和 shell 脚本,和写的代码完全脱节。 Zig 的处理是:构建过程本身就是一段 Zig 程序(build.zig),依赖清单是另一个文件(build.zig.zon)。 围绕这两个文件,这一章走完「格式化字符串 → 选分配器 → 写测试 → 测泄漏 → 出文档 → 引依赖 → 造库 → 发 GitHub → 基准对比」的整条链。

主线例子:zuuid,一个 UUID 生成器。先看它的 16 个随机字节怎么变成规范的版本 4 UUID, 再看同一份代码怎么从单文件工具拆成库、被别人当依赖引用、最后被拿去和系统自带的 uuidgen(1) 赛跑。

1. 顶层全景:一条「工具 → 库 → 包」的流水线

  单文件工具(zuuid.zig,能跑)
      │  把「生成逻辑」和「命令行界面」拆开
      ▼
  库(src/root.zig,可测、可被 import)
      │  GitHub 托管 + build.zig.zon 标明指纹、版本、最低 Zig 版本
      ▼
  包(别人在 build.zig.zon 里写你的 URL + 提交哈希 + 哈希值)
      │
      ▼
  消费者项目(zuuid-cli:zig build run 一条命令编译、链接、运行)

  图说:每一步都靠两个文件推进——build.zig 管「怎么编译」(Zig 代码),
  build.zig.zon 管「需要什么」(声明式清单)。

这一章真正想立的命题在结尾才明说:可复现构建、依赖隔离、测试纪律、内存泄漏验证、文档生成、基准测试、包发布,这些不是锦上添花,是基础设施软件的运行地基——「没法隔离测试的缓存服务器是负债;没有内存验证的并发索引是定时炸弹」1。

2. 核心原理

2.1 std.fmt:一个格式串,四种进制,两种落地方式

Zig 的字符串格式化集中在 std.fmt 一个命名空间下,而且在编译期就校验格式占位符和数据类型对不对得上,一整类运行时错误直接消失2。核心占位符:

占位符干什么例(对数字 255)
{d} {x} {b} {o}十进制 / 十六进制 / 二进制 / 八进制255 / ff / 11111111 / 377
{d:.2}保留两位小数3.14(对 π)
{d: >10.2}十位宽、右对齐、两位小数左边补空格的 3.14
{s}把字节按 UTF-8 文本打印Alice
{any}通用:递归打印任何类型的默认表示字符串会打成字节数组 { 65, 108, … }

图说:最后两行是那组容易踩的对比——{s} 把字节当文本,{any} 把字节当数值序列;书上 User{ .name = "Alice" } 被 {any} 打印成 .{ .name = { 65, 108, 105, 99, 101 }, .id = 101 },技术上完全正确(那正是 Alice 五个字母的 ASCII 码),但几乎从不是你要的3。经验法则:字符串用 {s},其余想看内容用 {any}。

格式化结果有两种落地方式,取舍清清楚楚:

  • std.fmt.allocPrint(allocator, …):算好精确长度、在堆上申请一块刚好够的内存——快,但要记得 defer free;
  • std.fmt.bufPrint(&buffer, …):写进你自己准备的栈数组(例子里是 64 字节),零堆分配、不用 free——更快,但输出超过容量就报 error.NoSpaceLeft,截断处理是你的责任4。

2.2 分配器不是设计偏好,是架构决策

Zig 取消了「全局默认分配器」。每个需要动态内存的函数都必须显式接收一个 std.mem.Allocator 参数;它只认这个接口,背后是系统堆、arena 还是栈上数组,它不知道也不关心——这个决定权在调用方,因为调用方才是唯一掌握足够上下文做对选择的人5。

接口本身简单到只有三个函数指针:申请(alloc)、调整大小(resize)、释放(free)。代价是每次分配多一次间接调用;买到的是「用内存的代码」和「管内存的代码」彻底解耦——也因此,你能写出包在任何分配器外面的日志分配器(记录每次申请释放)和故障注入分配器(第 N 次申请故意返回失败,系统性地测试每条失败路径)6。

书上一段 createNumbers(allocator) 函数连跑三次,分别喂三种分配器,输出一模一样——把内存引擎换了三遍,数据生成代码一行没动,这是这套接口设计成功的终极证明7。三种各司其职:

分配器交换什么什么时候用它
DebugAllocator(0.16 前的旧名 GeneralPurposeAllocator)用速度换正确性:跟踪每次分配,deinit() 时报告泄漏;debug 构建里用哨兵值毒化已释放内存,抓二次释放和释放后使用开发期默认
ArenaAllocator用「单个对象不能释放」换吞吐:内部一大块内存只前进指针,无锁无元数据;arena.deinit() 一次扫回全部有天然批量生命周期的负载:解析配置树用完就扔、每个请求建一堆对象响应完一起清
FixedBufferAllocator把「子分配器」整个去掉:给你已有的(通常是栈上的)数组当撞针分配器,实现就是一个指针加长度内存上限已知且小:嵌入式、热循环、序列化临时缓冲

图说:arena 那一行是重点——请求式系统(如 Web 服务)里「整棵对象图随请求结束瞬间销毁」,arena 把「我有没有漏 free 某个节点」这类问题整个消灭掉,因为这个问题根本不再被问起8。

DebugAllocator 还有两个细节值得记住:defer _ = gpa.deinit() 里的 _ = 只是压制「返回值没用」的编译警告,泄漏报告照样写 stderr;想在测试里把泄漏变成失败,就写 defer std.debug.assert(gpa.deinit() == .ok)9。

2.3 std.testing:测试住在源码里,泄漏算失败

Zig 的测试不是外部框架,是语言内置:test "名字" { … } 块就写在实现代码同一个文件里,只有执行 zig test 时才编译运行,生产二进制里被剥得干干净净10。断言语句三个就够用:expect(真值)、expectEqual(相等)、expectError(预期某个错误)。

测第 1 章的 zwhich 时冒出一个 0.16 特有的问题:main() 的 init.io 是运行时送的,测试块可没有运行时——书给的答案是 var t: std.Io.Threaded = .init_single_threaded 手动造一个最小 std.Io 实例11。测文件系统操作则用 testing.tmpDir(.{}) 开一个一次性隔离沙盒目录,defer tmp.cleanup() 保证不管测试通过、失败还是崩掉,整个目录被递归删除、不留痕迹12。

最有说服力的是可执行位那组断言:先用 fchmod 把测试文件设成 0o755(人人可执行),断言 isExecutable() 为真;再改成 0o644(主人可写、无人能执行),断言为假——同一个文件,翻转权限位,函数结论跟着翻转13。

泄漏检测是这节的真正主角:std.testing.allocator 跟踪测试期间每一次分配,测试块结束时强制审计,还有内存没还就整个测试判失败14。书上的实录极传神:

All 1 tests passed.
1 errors were logged.
1 tests leaked memory.
error: the following test command failed with exit code 1:

图说:数学全对——第 10 个斐波那契数确实是 34——但内务错了。Zig 把资源泄漏当成和逻辑错误同级的缺陷:数学对、家务烂,测试照样红15。书给出的理由值得背下来:漏几个字节的斐波那契函数无害,但同样模式放在长跑的服务器里,就是一个「几小时几天内慢慢耗尽内存、直到操作系统杀掉进程」的慢性事故16。

2.4 文档:/// 注释直接变成网站

Zig 把文档生成做进了编译器:给声明写三斜杠注释(///,双斜杠 // 编译器直接忽略),然后跑

zig build-exe -femit-docs zwhich.zig

编译器把所有三斜杠注释和函数签名抽出来,合成一个可搜索的静态 HTML 网站,文档永远和实现同步,因为它就是从实现里长出来的17。注释内容按 Markdown 解析:第一段当列表页摘要,后面的段落进详情页,还可以嵌 ```zig 代码块,让文档自带语法高亮的用法示例18。

一个反直觉的坑:生成的页面必须起一个本地 HTTP 服务(python3 -m http.server 8000)才能看——因为浏览器出于安全禁止 file:// 协议的页面发起 JavaScript fetch19。

2.5 build.zig.zon:用 URL 和哈希钉死「我依赖谁」

依赖清单 build.zig.zon(ZON 格式,长得和 Zig 的结构体字面量一样)声明的是 what:每个依赖不是一个模糊的名字,而是精确的 URL + 提交哈希 + 内容哈希20。

  • .url 用 git+https://…#<提交哈希> 的写法,把依赖钉死在历史的某一个精确快照上,上游 main 分支怎么变都伤不到你;
  • .hash 是下载内容本身的密码学校验——哪怕 Git 服务器被攻陷、对同一个提交返回了不一样的数据,Zig 也会拒绝;
  • .fingerprint 是 64 位标识,帮全局缓存快速认出这个项目,不是内容校验。

图说:这三个字段合起来保证「今天能编译的项目,将来以同样的方式编译」——这就是可复现构建的定义21。

书还演示了 Zig 的报错驱动工作流:故意写一份缺字段的清单,zig build 会接连报「缺 fingerprint,建议值是多少」「URL 缺明确的 ref,建议补上哪个提交」「缺 hash,期望值是多少」——编译器替你把清单补全,你只要照抄建议值22。

2.6 build.zig:构建图是一段程序

build.zig 回答 how。它的骨架就这几行,但每一行都在向一张「构建步骤的有向无环图」加一个节点:

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});      // 注册 -Dtarget=… 跨编译目标
    const optimize = b.standardOptimizeOption(.{});   // 注册 -Doptimize=ReleaseFast 等优化档
    const dep = b.dependency("包名", .{ .target = target, .optimize = optimize });
    const exe_mod = b.createModule(.{ .root_source_file = b.path("src/main.zig"), … });
    exe_mod.addImport("zuuid", dep.module("zuuid"));  // 源码里的 @import("zuuid") 指向谁
    const exe = b.addExecutable(.{ .name = "zuuid-cli", .root_module = exe_mod });
    b.installArtifact(exe);                            // 编完拷到 zig-out/bin
    …                                                  // 再注册一个 zig build run 步骤
}

图说:standardTargetOptions() 和 standardOptimizeOption() 这两行是跨编译的入口——同一个构建脚本,换 -Dtarget= 参数就能给别的操作系统和 CPU 出二进制;addImport 把「源码里 import 用的名字」和「依赖实际暴露的模块名」解耦,两个名字可以不一样23。好处书上说得直白:过去要记一长串 -I… -L… -lsqlite3 的命令,现在全存在 build.zig 里,zig build run -- 参数 一条命令编译、链接、带参数运行24。

2.7 zuuid:16 个随机字节怎么变成版本 4 UUID

UUID(通用唯一标识符)是一个 128 位的数,设计上让分布式系统各自生成、几乎不可能撞车;版本 4 是完全靠随机数造的那版,数据库主键、事务 ID 都爱用它25。zuuid 的生成循环短到可以逐行走:

第 1 步  var uuid: [16]u8 = undefined;
第 2 步  io.randomSecure(&uuid)        // 16 字节全部来自操作系统随机源
第 3 步  uuid[6]  = (uuid[6] & 0x0f) | 0x40   // 第 7 字节高 4 位钉成 0100 → 版本号 4
第 4 步  uuid[8]  = (uuid[8] & 0x3f) | 0x80   // 第 9 字节最高 2 位钉成 10 → 变体标识
第 5 步  按 8-4-4-4-12 分组打印成十六进制,单数位补零

图说:书上真实输出 44cb2754-b58d-428c-8c95-d6d299fb650c——看第 3 组 428c 的开头是 4(版本),第 4 组 8c95 的开头是 8(二进制 1000,最高两位正是 10,变体)。第 3、4 步那两个按位操作就是干这个的:先与掩码清掉要控制的位,再或上规定值26。

第 2 步的选择也经过推敲:io.random() 内部是伪随机数发生器,快但种子之外算法确定;io.randomSecure() 每次都真调操作系统随机源(getrandom 或 arc4random),可能失败(所以带 try),用于数据库 ID、会话令牌这类场景是对的取舍27。

2.8 从工具到库:一次有名字的架构动作

zuuid 单文件版把两件事揉在一起:生成 UUID 的逻辑,和把它打印到屏幕的命令行界面。书把这次重构的原则说得很明白:这两件事不是同一件事——生成逻辑不依赖 stdin/stdout/参数,是可复用、可单测、可基准测试的单元;CLI 只是它的一个消费者28。

拆完之后走完整生命周期:zig init 建骨架 → 把生成算法填进 src/root.zig → 测试搬进去(zig build test)→ 修掉 fingerprint 报错 → 推到 GitHub → 在另一个项目 zuuid-cli 的 build.zig.zon 里引用它(就是第 2.5 节的报错驱动流程)→ zig build run 跑起来29。

收尾是一场真基准:用 hyperfine 把 ReleaseFast 模式的 zuuid-cli 和系统自带 uuidgen(1) 各跑几千次,zuuid 平均 856.1 微秒对 uuidgen 的 1.4 毫秒,快 1.64 倍30。这组数字是「Zig 值得用在工具链上」主张的全章唯一硬证据。

2.9 测试过滤与覆盖率:两个调试放大器

  • zig test 文件 --test-filter "math":按测试名模式匹配,只跑带 "math" 的那几个——改某个函数时不被无关测试的噪音和延迟打扰31;
  • 覆盖率:先 -femit-bin 编出独立测试二进制,再用 kcov 跑它,生成把执行到的指令映射回源码行号的可视化报告——书上例子是 maybeDouble() 的 else 分支被标红:「翻倍」逻辑被验证了,「不翻倍」的行为还纯粹是理论32。过滤告诉你「选中的测试过了」,覆盖率告诉你「哪些代码根本没被跑到」。

3. 加厚:三个容易略过的好细节

3.1 计时器与微基准的写法

分配器基准用的是 0.16 的新计时习惯:std.Io.Timestamp.now(io, .awake) 取单调钟的当前时刻(.awake 表示「只算真实流逝的时间,不含进程睡着的部分」——性能测量要的就是这个),两个时刻相减用 .durationTo() 得 Duration,再取 .nanoseconds 变成可以累加求平均的整数33。每次试验重新打开文件、计时只包申请不包释放——把「要一块内存」的代价单独隔离,不让清理的波动混进数字34。

arena 的专用基准函数揭示了为什么接口统一还要单开一路:标准循环靠 free() 回收,而 arena 设计上忽略单个 free,标准循环会把内存攒到爆;arena 的回收姿势是 arena.reset(.retain_capacity)——把内部指针倒回起点、内存不还操作系统,第二轮起每轮复用同一块预分配内存,这才是游戏帧、请求处理器里「帧分配器」的真实用法35。

3.2 zuuid 打印的两位补零

UUID 十六进制打印有个细节:{x:0>2} 里的 0>2 是「宽度 2、右对齐、不足补零」——单数位十六进制必须补前导零,否则 16 字节会打印成不足 32 字符、分组全错位;长格式串用编译期 ++ 拼接换行,零运行时代价36。大写的 -u 版只是把 {x} 换成 {X},一个开关两条格式路径37。

3.3 hyperfine 跑的是什么

zuuid 那场 1.64 倍的对比,比的是单次冷执行:每次运行都从进程启动算到退出(856 µs 对 1.4 ms,各跑了几千次取平均)。它证明的是「Zig 写的命令行工具和系统自带工具同场不落下风」,不是吞吐量——真要比吞吐,得让进程活着连发请求38。读任何「快了多少倍」之前,先问这句倍率量的到底是什么形状。

3.4 构建步骤是一张有向无环图

build.zig 的每一行都在往一张图里加节点:standardTargetOptions() 注册 -Dtarget= 命令行旗标并解析成交叉编译约束;b.dependency() 查清单、把同一套 target/optimize 传进依赖(外库与主程序必须同架构同优化档编译,防不匹配);addImport 把源码里的 import 名映射到依赖实际暴露的模块名,两个名字可以不同;installArtifact 注册"副作用"步骤把二进制拷进 zig-out/bin;addRunArtifact 造一个虚拟运行命令,b.args 把 zig build run -- 参数 转发给程序本身39。图的好处:zig build run 一条命令把"编译 → 安装 → 带参运行"整条依赖链走一遍,每步只在脏的时候重跑。

3.5 测试过滤与覆盖率各管一半

--test-filter "math" 按测试名模式匹配,只跑带 "math" 的那几个——改某个函数时不被无关测试的噪音和延迟打扰40。覆盖率走另一条路:-femit-bin 先编出独立测试二进制,再用 kcov 跑它,生成把执行指令映射回源码行的可视化报告——书上的例子是 maybeDouble() 的 else 分支被标红:「翻倍」逻辑被验证了,「不翻倍」的行为还纯粹是理论41。过滤告诉你"选中的过了",覆盖告诉你"哪些根本没被跑到"——两半拼起来才是完整的验证画面。

4. 作者的判断与证据

  • 判断: 「分配器策略是架构决策,不是实现细节」——写在调用点的选择(arena/调试/栈缓冲)在代码里可见、在评审里可议、改起来不碰解析器本身42。证据:同一份 createNumbers() 换三种分配器输出不变。
  • 判断: 泄漏是「无声杀手」,资源泄漏应当与逻辑错误同级处理43。证据:std.testing.allocator 的强制审计 + 书上「TCP 代理每连接漏一块缓冲区,几小时后 OS 杀进程」的场景推演。
  • 判断: zuuid 比 uuidgen 快得多30。证据:hyperfine 双基准,3358 次对 2015 次运行。局限:只测了单进程冷启动级别的微基准,没测吞吐场景。
  • 推测: 「构建系统是基础设施」这个框是全章的价值观陈述,不是可证命题;支撑它的是结尾那串排比(负债、定时炸弹、维护负担),修辞多于论证。

4. 边界与局限

  • 覆盖率工具是外置的:kcov、hyperfine 都要另外装,Zig 自己只出测试二进制32。
  • 跨平台条件编译是显式标注的:fchmod 那组断言包在 if (os != .windows) 里,Windows 用访问控制列表而不是权限位——书正确处理了,但也意味着这套测试在 Windows 上覆盖更少13。
  • 依赖管理是「去中心化」的:没有 crates.io 那样的中央注册表,依赖就是 URL+哈希,上游仓库消失或改历史会让老项目装不上21。
  • zuuid 的实现是教学级:没有处理时钟回拨、没有 v1/v5,只做了 v425。

5. 可带走的

  1. 字符串格式化:{s} 给人看,{any} 给调试看;栈缓冲用 bufPrint,堆上用 allocPrint 且必须 defer free。
  2. 分配器三选一的决策树:开发期 DebugAllocator(报泄漏)、批量生命周期 ArenaAllocator(免逐个 free)、上限已知 FixedBufferAllocator(零开销)。
  3. 想把分配器换策略?函数只收 std.mem.Allocator,调用点换实参,一行的事。
  4. 测试块和实现住同一个文件;std.testing.allocator 让「数学对、内存漏」的测试直接红。
  5. 测文件系统用 testing.tmpDir 沙盒,defer cleanup 保证不留痕。
  6. 文档从 /// 注释长出来,-femit-docs 一键出网站;本地看要起 HTTP 服务,file:// 打不开。
  7. build.zig.zon 三件套:URL 钉提交、hash 验内容、fingerprint 认项目;报错驱动,缺什么编译器告诉你。
  8. build.zig 五步:目标/优化档 → 依赖 → 模块 → 工件 → 步骤;zig build run -- 参数 一次跑通。
  9. 学一个库的生命周期:工具拆库 → 测试进库 → GitHub 托管 → 别人 zon 引用 → hyperfine 对上系统工具。
  10. --test-filter 缩小循环,kcov 找红色死区——过滤管「跑哪些」,覆盖管「漏了谁」。

6. 原文地图

主题原书章原文位置
{any} 与 {s} 的区别Chapter 2: Systems Infrastructure: Builds, Testing, and Distributiontext/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:361(搜「technically correct」)
allocPrint / bufPrint 取舍Chapter 2: Systems Infrastructure: Builds, Testing, and Distributiontext/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:322(搜「NoSpaceLeft」)
分配器接口与调用方决定权Chapter 2: Systems Infrastructure: Builds, Testing, and Distributiontext/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:475(搜「only party with enough context」)
日志/故障注入分配器Chapter 2: Systems Infrastructure: Builds, Testing, and Distributiontext/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:617(搜「logging allocator」)
三种分配器同一函数Chapter 2: Systems Infrastructure: Builds, Testing, and Distributiontext/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:1037(搜「swapped the underlying」)
测试块住在源码里Writing unit tests with std.testingtext/23-fm-writing-unit-tests-with-std-testing.txt:29(搜「same source files」)
tmpDir 沙盒Writing tests for zwhichtext/24-fm-writing-tests-for-zwhich.txt:120(搜「strictly isolated temporary directory」)
0o755/0o644 可执行位翻转Writing tests for zwhichtext/24-fm-writing-tests-for-zwhich.txt:262(搜「0o755」)
泄漏测试实录Detecting memory leaks automatically during teststext/25-fm-detecting-memory-leaks-automatically-during-test.txt:111(搜「All 1 tests passed」)
泄漏=临界缺陷Detecting memory leaks automatically during teststext/25-fm-detecting-memory-leaks-automatically-during-test.txt:141(搜「critical defects」)
/// 注释生成文档Generating documentation (-femit-docs)text/26-fm-generating-documentation-femit-docs.txt:15(搜「triple-slash」)
file:// 看不了文档Generating documentation (-femit-docs)text/26-fm-generating-documentation-femit-docs.txt:261(搜「security reasons」)
zon 的 URL+哈希钉死Managing dependencies with build.zig.zontext/28-fm-managing-dependencies-with-build-zig-zon.txt:110(搜「cryptographic integrity check」)
build.zig 五步骨架Understanding build.zigtext/29-fm-understanding-build-zig.txt:76(搜「Directed Acyclic Graph」)
UUID v4 定义Compiling a library to create an executable binarytext/30-fm-compiling-a-library-to-create-an-executable-bina.txt:176(搜「constructed almost entirely from random numbers」)
两个按位操作钉版本与变体Compiling a library to create an executable binarytext/30-fm-compiling-a-library-to-create-an-executable-bina.txt:392(搜「precise bitwise operations」)
randomSecure 与 random 之别Compiling a library to create an executable binarytext/30-fm-compiling-a-library-to-create-an-executable-bina.txt:390(搜「PRNG seeded from a secure source」)
工具拆库的原则Compiling a library to create an executable binarytext/30-fm-compiling-a-library-to-create-an-executable-bina.txt:453(搜「two distinct concerns」)
zuuid 对 uuidgen 基准Benchmarking zuuid against uuidgentext/35-fm-benchmarking-zuuid-against-uuidgen.txt:24(搜「1.4 ms」)
测试过滤Test filtering and coverage analysistext/36-fm-test-filtering-and-coverage-analysis.txt:48(搜「test-filter」)
kcov 覆盖死区Test filtering and coverage analysistext/36-fm-test-filtering-and-coverage-analysis.txt:81(搜「else」)
「基础设施」价值观收尾Test filtering and coverage analysistext/36-fm-test-filtering-and-coverage-analysis.txt:165(搜「operational foundations」)

本章拆解依据原书第 2 章(含各小节)。每条出处可用 node scripts/book-verify.mjs systems-programming-with-zig 回核。

Footnotes

  1. 出处:「Test filtering and coverage analysis」第 165 段(text/36-fm-test-filtering-and-coverage-analysis.txt:165,搜「operational foundations」)。 ↩

  2. 出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 83 段(text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:83,搜「compile-time logic」)。 ↩

  3. 出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 361 段(text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:361,搜「technically correct」)。 ↩

  4. 出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 322 段(text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:322,搜「NoSpaceLeft」)。 ↩

  5. 出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 475 段(text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:475,搜「only party with enough context」)。 ↩

  6. 出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 617 段(text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:617,搜「logging allocator」)。 ↩

  7. 出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 1037 段(text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:1037,搜「swapped the underlying」)。 ↩

  8. 出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 561 段(text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:561,搜「natural batch lifetime」)。 ↩

  9. 出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 892 段(text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:892,搜「assert that no leaks occurred」)。 ↩

  10. 出处:「Writing unit tests with std.testing」第 29 段(text/23-fm-writing-unit-tests-with-std-testing.txt:29,搜「same source files」)。 ↩

  11. 出处:「Writing tests for zwhich」第 56 段(text/24-fm-writing-tests-for-zwhich.txt:56,搜「std.Io.Threaded」)。 ↩

  12. 出处:「Writing tests for zwhich」第 120 段(text/24-fm-writing-tests-for-zwhich.txt:120,搜「strictly isolated temporary directory」)。 ↩

  13. 出处:「Writing tests for zwhich」第 262 段(text/24-fm-writing-tests-for-zwhich.txt:262,搜「0o755」)。 ↩ ↩2

  14. 出处:「Detecting memory leaks automatically during tests」第 15 段(text/25-fm-detecting-memory-leaks-automatically-during-test.txt:15,搜「mandatory audit」)。 ↩

  15. 出处:「Detecting memory leaks automatically during tests」第 111 段(text/25-fm-detecting-memory-leaks-automatically-during-test.txt:111,搜「All 1 tests passed」)。 ↩

  16. 出处:「Detecting memory leaks automatically during tests」第 155 段(text/25-fm-detecting-memory-leaks-automatically-during-test.txt:155,搜「slow-motion failure」)。 ↩

  17. 出处:「Generating documentation (-femit-docs)」第 15 段(text/26-fm-generating-documentation-femit-docs.txt:15,搜「triple-slash」)。 ↩

  18. 出处:「Generating documentation (-femit-docs)」第 66 段(text/26-fm-generating-documentation-femit-docs.txt:66,搜「Markdown」)。 ↩

  19. 出处:「Generating documentation (-femit-docs)」第 261 段(text/26-fm-generating-documentation-femit-docs.txt:261,搜「security reasons」)。 ↩

  20. 出处:「Managing dependencies with build.zig.zon」第 110 段(text/28-fm-managing-dependencies-with-build-zig-zon.txt:110,搜「cryptographic integrity check」)。 ↩

  21. 出处:「Managing dependencies with build.zig.zon」第 100 段(text/28-fm-managing-dependencies-with-build-zig-zon.txt:100,搜「always fetch that exact snapshot」)。 ↩ ↩2

  22. 出处:「Using our zuuid library」第 48 段(text/34-fm-using-our-zuuid-library.txt:48,搜「error-driven workflow」)。 ↩

  23. 出处:「Understanding build.zig」第 76 段(text/29-fm-understanding-build-zig.txt:76,搜「Directed Acyclic Graph」)。 ↩

  24. 出处:「Compiling a library to create an executable binary」第 94 段(text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:94,搜「avoid retyping」)。 ↩

  25. 出处:「Compiling a library to create an executable binary」第 176 段(text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:176,搜「constructed almost entirely from random numbers」)。 ↩ ↩2

  26. 出处:「Compiling a library to create an executable binary」第 392 段(text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:392,搜「precise bitwise operations」)。 ↩

  27. 出处:「Compiling a library to create an executable binary」第 390 段(text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:390,搜「PRNG seeded from a secure source」)。 ↩

  28. 出处:「Compiling a library to create an executable binary」第 453 段(text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:453,搜「two distinct concerns」)。 ↩

  29. 出处:「Using GitHub」第 17 段(text/31-fm-using-github.txt:17,搜「zig init」)与「Using our zuuid library」第 341 段(text/34-fm-using-our-zuuid-library.txt:341,搜「zig build run」)。 ↩

  30. 出处:「Benchmarking zuuid against uuidgen」第 24 段(text/35-fm-benchmarking-zuuid-against-uuidgen.txt:24,搜「1.4 ms」)。 ↩ ↩2

  31. 出处:「Test filtering and coverage analysis」第 48 段(text/36-fm-test-filtering-and-coverage-analysis.txt:48,搜「test-filter」)。 ↩

  32. 出处:「Test filtering and coverage analysis」第 79 段(text/36-fm-test-filtering-and-coverage-analysis.txt:79,搜「flag the」)。 ↩ ↩2

  33. 出处:「Reading Little-Endian integers from raw bytes」第 742 段(text/41-fm-reading-little-endian-integers-from-raw-bytes.txt:742,搜「std.Io.Timestamp.now」)。 ↩

  34. 出处:「Reading Little-Endian integers from raw bytes」第 810 段(text/41-fm-reading-little-endian-integers-from-raw-bytes.txt:810,搜「cost of asking」)。 ↩

  35. 出处:「Reading Little-Endian integers from raw bytes」第 836 段(text/41-fm-reading-little-endian-integers-from-raw-bytes.txt:836,搜「retain_capacity」)。 ↩

  36. 出处:「Compiling a library to create an executable binary」第 422 段(text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:422,搜「leading zero」)。 ↩

  37. 出处:「Compiling a library to create an executable binary」第 412 段(text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:412,搜「uppercase」)。 ↩

  38. 出处:「Benchmarking zuuid against uuidgen」第 25 段(text/35-fm-benchmarking-zuuid-against-uuidgen.txt:25,搜「2015 runs」)。 ↩

  39. 出处:「Understanding build.zig」第 100 段(text/29-fm-understanding-build-zig.txt:100,搜「register command-line flags」)。 ↩

  40. 出处:「Test filtering and coverage analysis」第 48 段(text/36-fm-test-filtering-and-coverage-analysis.txt:48,搜「test-filter」)。 ↩

  41. 出处:「Test filtering and coverage analysis」第 79 段(text/36-fm-test-filtering-and-coverage-analysis.txt:79,搜「flag the」)。 ↩

  42. 出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 627 段(text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:627,搜「architectural decision」)。 ↩

  43. 出处:「Writing tests for zwhich」第 382 段(text/24-fm-writing-tests-for-zwhich.txt:382,搜「silent killers」)。 ↩

On this page