Chapter 00Zigbook_introduction

引言

抽象的目的并非为了模糊不清,而是为了创造一个新的语义层次,在其中人们可以做到绝对的精确。

Edsger W. Dijkstra

欢迎来到Zig的世界

大多数编程语言都会向你隐藏复杂性——它们抽象掉内存管理,用隐式操作掩盖控制流,并将你与底层机器隔离开来。起初,这感觉很简单,但最终你会碰壁。你需要理解为什么某个东西很慢,在哪里发生了崩溃,或者如何从硬件中榨取每一丝性能。突然之间,那些曾帮助你入门的抽象现在成了你的障碍。

Zig选择了一条不同的道路。它揭示复杂性,然后给你掌握它的工具。

这本书将带你从Hello, world!开始,到构建可以交叉编译到任何平台、以手术刀般的精度管理内存并在编译时生成代码的系统。你不仅将学会如何使用Zig,还将明白为什么它会以这种方式工作。每一次内存分配都将是明确的。每一个控制路径都将是可见的。每一个抽象都将是精确的,而非模糊的。

读完这六十一章后,你将不仅仅是了解Zig。你将对系统编程有一个深刻的理解,这种理解会让你觉得其他语言似乎对你有所隐瞒。因为它们确实如此。

这段旅程始于第一天遇到的那种简单。到最后,你会发现另一种简单:那种通过攀登复杂性的阶梯,并最终获得完全理解后所赢得的简单。

欢迎来到Zigbook。你的蜕变现在开始。

你将成为什么样的人

学习Zig不仅仅是在你的简历上增加一种语言。它将从根本上改变你对软件的思考方式。

读完这本书后,你将能够:

  • 完全理解你的程序。 你将知道内存中的每一个字节位于何处,编译器何时执行你的代码,以及你的抽象编译成了什么机器指令。没有隐藏的内存分配。没有神秘的开销。没有意外。
  • 控制整个技术栈。 从裸机嵌入式系统到浏览器中的WebAssembly,从内核模块到网络服务——你将拥有一个工具链,一种语言,并完全控制你的代码在任何地方的运行方式。
  • 自信地进行调试。 当出现问题时,你将不再是猜测。你将阅读堆栈跟踪,检查内存布局,验证分配器行为,并用构建Zig编译器本身的相同工具精确定位问题。
  • 构建可靠的系统。 通过显式错误处理、资源清理保证以及在开发过程中捕捉错误而不牺牲发布性能的安全模式,你将交付可以信赖的代码。
  • 为未来做出贡献。 Zig还很年轻,在不断发展,并渴望贡献者。你将有基础去提议新功能、修复错误、编写库,并帮助塑造一种重视清晰和正确的语言。

你将成为那种看到垃圾回收器就会想“我能做得更好”的开发者。那种无畏阅读汇编语言的人。那种无需安装独立工具链就能交叉编译到新架构的人。那种不仅知道什么可行,而且理解为什么可行的开发者。

这无关乎记住语法。这关乎赢得精通。

关于本书

Zigbook特意不包含任何AI生成的内容——它是手写的,精心策划的,并持续更新以反映最新的语言特性和最佳实践。

作者的简短说明:

你好,读者!

感谢你选择Zigbook作为学习Zig的指南。我谨正式邀请Zig社区为Zigbook做出贡献。无论你发现一个拼写错误,想改进一个解释,还是有更好的方式来演示一个概念,你的贡献都会帮助每一个从这本书中学习的人。

你可以通过这里的issue或pull request来做出贡献。

请注意: 我会亲自审查每一份提交,以确保准确性和清晰度。我们可以一起为未来的Zig开发者把这个资源做得更好。

Zigbook最初由@zigbook编写,他是一位经验丰富的系统程序员和Zig社区成员,旨在填补现有资源的空白,并与他人分享这些知识。

此后,它已发展成为一本全面的Zig编程语言指南,其结构是从基础知识到高级系统编程的旅程。它专为那些希望理解而不仅仅是使用的开发者而设计;他们重视透明胜于魔法,精确胜于便利。

Zigbook通过提供深入的解释、实践项目和精心策划的学习路径来补充官方文档。在语言参考告诉你一个特性做什么的地方,这本书会告诉你何时使用它,为什么它很重要,以及如何将它融入真实世界的代码中。

结构: Zigbook分为七个部分,交替出现概念章节(教学)和项目章节(应用)。早期章节有意推迟深入探讨,直到你有了理解它们的基础。后面的章节则假设你已经内化了早期的材料。这是一条路径,而不是一本参考手册:第一次请按顺序阅读,之后再用作参考。

先决条件: 你应该至少熟悉一种编程语言和基本的命令行操作。有C、C++或Rust的经验会帮助你进行比较,但这不是必需的。如果你愿意深入研究这些概念,Zig可以成为你的第一门系统语言。

什么是Zig?

Zig是一种系统编程语言,专为需要完全控制、高效率和简单性,同时又不牺牲安全性或性能的开发者而设计。它将自己定位为一个“没有意外”的工具链:每一个控制路径、内存分配和优化决策都是你可以跟踪、修改或选择退出的。

Zig反映了C语言的直接性,同时加入了现代标准库、更好的编译时保证和一流的交叉编译支持。该语言有意避免“魔法”特性——没有隐藏的控制流,没有垃圾回收器,没有强制的运行时——所以你可以审查二进制文件并确切地理解你的代码编译成了什么。

核心哲学

Zig的使命围绕着清晰和机械同理心。编译器相信你能做出正确的决定,同时在开发过程中提供安全网。调试构建会捕捉溢出、释放后使用和其他错误。发布构建会移除这些检查以获得最高性能。你通过构建模式明确地选择权衡,而不是通过语言层面的妥协。

标准库拥抱直截了当的构建块:文件即模块、显式分配器和类型化错误。新手无需记住庞大的框架就能理解代码。这种简单性延伸到了工具——zig buildzig testzig run处理大多数工作流,而build.zig脚本只是Zig代码,而不是一种独立的配置语言。22

Zig如何与其他语言比较

Zig和C: Zig继承了C的“你说了算”的哲学,同时移除了未定义行为的陷阱。检查过的算术、带标签的联合、可选类型和显式错误处理取代了C的静默失败。你用现代语法和更好的诊断获得了同等级别的控制。

Zig和Rust: Rust在编译时通过借用检查来强制安全,而Zig提供手动控制和可选的运行时检查。你决定何时生命周期重要,何时性能胜过静态强制执行。Zig的学习曲线更平缓——需要掌握的语言特性更少,尽管你承担了更多责任。17

Zig和Go/Python:与垃圾回收语言相比,Zig让你对内存和性能有精细的控制。其简单性和显式分配器使其成为嵌入式系统、内核和性能关键路径的理想选择。但Zig的覆盖范围超出了传统的系统编程——开发者用它来开发CLI工具、游戏、WebAssembly模块和高性能网络服务。41

Zig不试图成为所有人的万能语言。它选择透明胜于便利,显式胜于推断,理解胜于抽象。如果你重视确切地知道你的代码做了什么,Zig就是你的语言。

Zig为你提供了什么

四个能力定义了Zig的体验,并贯穿本书:v0.15.2

  1. 无隐藏的控制流。 编译器从不注入分配器、goroutine或隐式析构函数。机器码直接对应你所写的代码。当你阅读Zig代码时,你确切地知道将执行什么。

  2. 带护栏的手动内存管理。 分配器API是一等参数,而不是隐藏的运行时机制。调试和ReleaseSafe模式在开发过程中捕捉双重释放、释放后使用和缓冲区溢出。ReleaseFast模式为生产环境剥离了这些检查。你来控制这种权衡。10

  3. 编译时执行。 任何函数都可以在comptime运行,将编译器变成一个元编程引擎。生成查找表、验证模式或定制通用API,所有这些都在二进制文件发布之前完成。零运行时成本,完全的语言访问。15

  4. 轻松的交叉编译。 捆绑的工具链通过一个命令就能针对数十个操作系统/架构对。没有独立的工具链,没有交叉编译SDK,没有配置文件——只需-target即可。41

这些不是营销幻灯片上的要点——它们是塑造你如何编写、调试和部署Zig代码的原则。你将在每一章中遇到它们,从Hello, world!到构建你自己的分配器。

快速入门

从下载到执行Zig代码只需几个步骤。本书的其他所有内容都假设你的PATH上有这个工具链。官方的下载页面为Linux、macOS和Windows提供了发布版二进制文件。像Homebrew这样的包管理器和流行的Linux发行版会跟踪最新的稳定版本,但直接获取tarball或zip文件可以保证与本书中的示例版本一致。解压后,确认你的安装:

Shell
$ zig version
0.15.X

如果zig version报告了一个更早的版本,请重新进行下载步骤,以便后续章节中的示例与v0.15.2+中引入的安全模式行为保持一致。

你的第一个程序

编译并运行你的第一个main函数,以验证工具链和标准库是否按预期工作。创建一个名为hello_world.zig的文件,内容如下:

Zig
const std = @import("std");

pub fn main() void {
    std.debug.print("Hello, world!\n", .{});
}
运行
Shell
$ zig run hello_world.zig
输出
Shell
Hello, world!

std.debug.print写入到stderr。第一章在你关心输出通道和系统调用时,将探讨缓冲的stdout写入器。1

探索工具表面

即使是这个最小的例子也展示了Zig统一的工具故事:同一个zig run命令处理编译、链接和执行,而zig testzig build扩展了工作流,而无需更改语言。22将你的代码保存在main.zig或你传递给CLI的任何文件名中;根模块就是你调用的任何文件。

一个交互式循环

“Hello, world!”正常工作后,将程序扩展为一个简单的循环,以见证Zig的显式控制流和格式化语法,如#while中所述。

Zig
const std = @import("std");

pub fn main() void {
    var i: u32 = 1;
    while (i <= 10) : (i += 1) {
        std.debug.print("{d} squared is {d}\n", .{ i, i * i });
    }
}
运行
Shell
$ zig run squares_demo.zig
输出
Shell
1 squared is 1
2 squared is 4
3 squared is 9
4 squared is 16
5 squared is 25
6 squared is 36
7 squared is 49
8 squared is 64
9 squared is 81
10 squared is 100

Zig的while循环允许内联增量子句,while (cond) : (update),这使得移植C风格的循环变得容易,而无需引入隐藏的迭代器。

前路漫漫

你现在有了一个可用的Zig工具链和两个小程序。这是基础。接下来的一切都建立在这一刻之上——你第一次编译Zig代码并看到它运行。

下一章将介绍Zig如何将源文件视为模块,入口点如何传播错误,以及构建模式如何将相同的代码转换为不同的安全性和性能配置。你将学到main并非魔法:它是由std.start发现的,如果需要,你可以绕过它。

第61章,你将不仅了解Zig;你将深刻理解它,足以教导他人,为生态系统做出贡献,并构建反映你完全掌握的系统。

这段旅程始于简单。它以另一种简单结束:那种通过理解赢得的简单。

你的蜕变现在开始。翻开下一页。

Help make this chapter better.

Found a typo, rough edge, or missing explanation? Open an issue or propose a small improvement on GitHub.