← 返回 学习

有了 C++、D 和 Rust,为什么还需要 Zig?

没有隐式控制流

如果 Zig 代码看起来不像是在调用一个函数,那么它就不是。这意味着你可以确定下面的代码只会先调用 foo(),然后调用 bar(),不需要知道任何元素的类型,这一点也是可以保证的:

var a = b + c.d;
foo();
bar();

隐式控制流的例子:

这个设计决定的目的是可读性。

没有隐式内存分配

Zig 语言不干预堆内存分配。没有new关键字或其他任何使用堆分配器的语言功能(例如字符串连接运算符[1])。整个堆都是由库或者用户代码而非语言本身所管理的。

隐式内存分配的例子:

几乎所有的包含垃圾收集的语言都充满了隐式内存分配,不过垃圾收集器把隐式内存分配的证据隐藏在清理的那一侧。

隐藏内存分配的主要问题在于,它阻止了一段代码的可重用性,从而不必要的限制了适合代码部署的环境数量。简而言之,在某些用例中,必须能依赖于控制流和函数调用不产生内存分配的副作用,因此,一门语言必须能在切实提供这些保证的情况下才能为这些用例提供服务。

在 Zig 中,有一些标准库功能提供了堆分配器并且可以配合堆分配器,但这些都是可选的标准库特性,而不是内置在语言本身中的。如果你从不初始化堆分配器,那么你可以确信你的程序永远不会引起堆分配。

每一个需要分配内存的标准库特性都会接受一个分配器参数来进行内存分配。这意味着 Zig 的标准库特性支持裸金属目标。例如 std.ArrayListstd.AutoHashMap都可以用于裸金属编程!

自定义内存分配器使得手动管理内存变得轻而易举。Zig 有一个调试目的的分配器,可以在“释放后使用”和“双重释放”的情况下保证安全性。它能自动检测,并在内存泄露的时候打印堆栈跟踪;还有一个 Arena 分配器,可以让你将多个分配请求合并成一个,并统一释放,而不是独立的释放。特殊用途的分配器可以用来提高性能或内存的使用,以满足任何特定应用程序的需要。

[1]:事实上有一个编译期字符串连接运算符(广义来说,是数组连接运算符),但它只能在编译期使用,所以仍然没有运行时的堆分配。

无标准库的一流支持

如上所述,Zig 具有完全可选的标准库。每个标准库 API 仅在使用时才会编译到你的程序中。Zig 同时支持链接或不链接 libc。因此 Zig 非常适合裸机和高性能开发。

这是两全其美的。例如在 Zig 中,与支持编译为 WebAssembly 的其他编程语言相比,WebAssembly 程序既可以使用标准库的常规功能,又可以生成最小的二进制文件。

为库设计的可移植语言

编程的圣杯之一是代码重用。遗憾的是,在实践中我们发现自己多次重复发明轮子。很多时候这是有理由的:

为现有项目的构建系统和包管理器

Zig 不仅是一门编程语言,还是一个工具链。它附带了一个构建系统和包管理器,即使在传统的 C/C++ 项目环境中也非常有用。

你不仅可以用 Zig 代码代替 C 或 C++ 代码,还可以用 Zig 代替 autotools、cmake、make、scons、ninja 等。此外,它还提供了一个用于管理原生依赖的包管理器。即使项目的整个代码库都是 C 或 C++,这个构建系统也是适用的。例如,通过将 ffmpeg 移植到 Zig 构建系统,只需下载 50 MiB 的 Zig,就可以在任何支持的系统上编译 ffmpeg,以供任何支持的系统使用。对于开源项目来说,这种从源代码构建甚至跨平台编译的简化能力,可能是吸引或失去宝贵贡献者的关键。

apt-get、pacman、homebrew 等系统包管理器对最终用户的体验很有帮助,但它们可能不足以满足开发人员的需求。有无语言专用的包管理器可以形成没有贡献者和有大量贡献者之间的差距。对于开源项目来说,项目构建的难度对潜在贡献者来说是一个巨大的障碍。特别是对于 C/C++ 项目来说,依赖关系可能是致命的,尤其是在没有包管理器的 Windows 上。即使只是构建 Zig 本身,大多数潜在的贡献者在 LLVM 依赖上也会遇到困难。Zig(将)为项目提供一种直接依赖原生库的方式——不需要依赖用户的系统包管理器来获得正确的版本,而且这种方式几乎可以保证不管使用的是什么系统,也不管目标平台是什么,都可以在第一次尝试时就成功地构建项目。

其他编程语言虽然有包管理器,但它们并不能像 Zig 那样消除麻烦的系统依赖。

Zig 用一种使用声明式 API 的合理的语言来代替项目的构建系统。它还提供包管理系统,从而可以依赖其他 C 库。有了声明依赖的能力,就能实现更高层次的抽象,从而实现可重用高级代码的大量涌现。

简单性

C++、Rust 和 D 有大量的特性,它可能会打乱你正在编写的应用程序的实际含义。人们发现自己是在调试自己的编程语言知识,而不是调试应用程序本身。

Zig 没有宏也没有元编程,但仍然足够强大,可以清晰、不重复地表达复杂的程序。即使是在有宏的 Rust 里,format! 也是特例,它是在编译器内部实现的。与此同时 Zig 中的等价函数是在标准库中实现的,编译器中没有特例代码。

工具

可以从下载页面下载 Zig。Zig 提供了 Linux、Windows 和 MacOS 的二进制存档。你将得到: