编程语言之Zig
2019-10-09 08:40:16 阿炯

Zig 是一门通用编程语言,专为稳定性、可维护性和性能而设计,追求替代C语言在系统编程上的最佳地位。采用MIT许可协议授权。


Zig is a general-purpose programming language designed for robustness, optimality, and maintainability. Zig is aggressively pursuing its goal of overthrowing C as the de facto language for system programming. Zig intends to be so practical that people find themselves using it, because it "just works".

特性:
手动管理内存;
与 C 语言竞争而非依赖它,Zig 标准库不依赖于 libc;
轻量而简单,专注于调试应用而不是调试编程语言的知识;
新的错误处理方法,与编写良好的 C 语言错误处理类似,但减少了很多冗余;
调试模式下优化了快速编译时间,并在不确定行为发生时使用堆栈跟踪崩溃;
ReleaseFast 模式和 ReleaseSafe 模式;
泛型数据结构和函数,通过协程实现并发;
导入 .h 头文件并直接使用 C 语言的类型、变量和函数;
导出要依赖 C 语言代码的函数,变量和类型,自动生成 .h 头文件;
可选类型而非空指针,交叉编译是主要用例。

语言特性层面的主要更新包括:
切片类型不再具有字段访问,而是使用@typeInfo(Slice).Pointer.child。
枚举文字隐式 cast 为带标记的 union。
允许将可选指针隐式 cast 为可选c_void指针。
添加了*[N]T隐式 cast 到[*c]T。
anyerror不再作为关键字。
@cmpxchgStrong 和 @cmpxchgWeak 现在在 x86_64 上支持 128 位整数。
comptime_int现在隐式 cast 为comptime_float。
@typeOf 现在保证没有运行时副作用。
noinline函数声明中添加了关键字。
现在可以比较并集标记和枚举文字。
单元素enum默认设置为u0,comptime_int仍允许作为显式枚举标签类型。

Zig 的吉祥物“零号(Zero the Ziguana)”



与其它语言的对比

C尽管有其所有局限性,但它是一种相当简单的语言,可以提供很多控制。

Go和Rust都真的接近于替换C,Go摆脱了使用C的简单性和使用感,但是它使用垃圾回收并不能完全替代C。与Java相比,在Go中对内存使用的更多控制仍然是毫无价值的,因为您可以获得指针,并且实际上可以创建自己的辅助分配器。

Rust降低了手动的内存分配但未能复制C的简单性和感觉。也许这两种语言之间是否有什么可以填补的空间?

确实有,它可能就是Zig。Zig比Go更复杂,但比Rust更易于学习和使用。Zig为表带来了很多新想法,并且使Zig编码的体验非常独特。

Zig没有提供Rust所提供的那种最高的安全性,但是如果不这样做,它所获得的是一个对于初学者来说更容易掌握和使用的模型。需要在Zig中分配内存的任何内容都将分配器作为参数,因此Zig非常明确地说明了何时需要内存管理。它是一门强大而灵活的系统级编程语言,适用于各种系统级编程任务。它提供了简单、安全和高效的编程体验,具备优秀的类型安全和错误检测功能。通过学习和掌握Zig,你可以扩展你的编程技能,并在相关领域中应用它。它为开发者提供了简单、安全和高效的编程体验,具备现代语言的特性和传统低级语言的控制能力。学习Zig的方式包括阅读官方文档、浏览示例代码、参与练习项目和社区讨论。Zig的应用领域广泛,包括系统级开发、性能优化、跨平台开发和工具开发等。通过掌握Zig,将能够在底层软件开发领域中脱颖而出,并为你的编程能力注入新的活力。其适用于各种系统级开发任务,包括操作系统、驱动程序、编译器、嵌入式系统和网络协议栈等。其高级的类型系统和错误检测功能使得开发者能够更轻松地编写安全可靠的底层软件。此外,Zig还可用于性能优化、跨平台开发和工具开发等领域,为开发者提供了广阔的应用空间。

Zig的语法类似于C语言,但在类型安全和错误检测方面更加强大。它支持静态类型检查、模块化编程、自动内存管理和编译时错误检测等特性。Zig鼓励显式编程,强调代码的可读性和可维护性。它还提供了对内存布局和控制流的细粒度控制,使开发者能够精确地管理资源和优化性能。作为一种系统级编程语言,Zig可以用于开发底层的软件,如操作系统、编译器、嵌入式系统等。这些领域的专业知识和技能通常是高度需求的,因此熟练掌握Zig编程语言可能会为你提供一些就业机会。其被设计成易于学习和使用。如果使用过任何类似C的语言,那么其学习曲线就会显着缩短;它尝试保留 C 的大部分现有语法,只在必要时引入新语法。这让 Zig 变得对初学者友好。

用 Rust 构建的最大的应用程序之一是 Linux 内核,Rust 已被 Linux 社区正式采用,用于积极构建和维护 Linux 内核,这是该语言的一个重要里程碑,它表明 Rust 能够支持最苛刻的应用。Rust编译器本身也是用Rust编写的,Parity 是用 Rust 编写的以太坊区块链的客户端。Rust 也被许多公司在其生产系统中使用,包括亚马逊、Facebook、谷歌和微软。目前使用 Zig 构建的最热门的应用程序是 Bun Javascript 运行时,这是一个在服务器上执行 Javascript 的运行时,它与 NodeJS 和 Npm 完全兼容。Zig编译器也是用Zig编写的,Uber也使用Zig C++编译器通过Hermetic CC工具链在arm64硬件上运行Uber服务。


C及其替代方案:使用小型的高性能语言,如传统的 C、Go、Zig 或 Nim。

C++及其替代方案:使用有点复杂、高性能的语言,如传统的C++、Rust、Carbon 或 Cppfront。

Zig属于第一种。它是一种小型的、类似C语言的高性能语言,没有专用的运行时(没有内置的垃圾收集器)。Zig试图通过解决C开发人员面临的问题来成为“更好的C语言”,而Rust试图成为“更好的C++”语言。

没有专用运行时的语言,如C、C++、Rust和Zig,都会从用特定语言编写的每个源代码生成原始汇编,因此系统编程语言的性能取决于二进制文件中汇编代码的质量。系统编程语言的编译器要么使用LLVM优化,要么使用它们自己的成熟优化,因此很难说哪种语言更快——系统编程性能通常取决于程序员编写的算法。

C语言现在有一个标准规范,也有了几个实现。例如可以在GNU/Linux系统上使用GNU C编译器,在macOS上使用Apple Clang,在Windows上使用MSVC。C也有多个标准库实现,如libc、Microsoft C运行库、BSD libc、musl、Bionic等。一个编译器实现和一个基本的标准库是不足以高效地构建软件系统的——C程序员需要使用构建系统、包管理器、测试运行器等。对于这些工具,要么必须使用现有的第三方工具,要么编写自己的工具。

Zig语言也接近硬件层,但它实现了各种快捷方式和现代语言概念(即,切片、内置函数、循环简写等),为系统编程提供了一种高效的语言。其功能齐全的模块化标准库帮助Zig超越了系统编程。它提供了在通用编程语言中寻求的控制结构、数据结构和基本算法,这也非常适合系统编程。其工具链的特性,如交叉编译和构建系统API,激励程序员在现代系统编程中优先选择Zig;对于现有的C代码库,Zig工具链提供了一个可替换的C/C++编译器和C互操作,因此程序员可以将传统的C代码库增量地迁移到现代的Zig。

V编程语言的创建者Alexander Medvednikov进行的对比测试,这是编译具有400K函数的文件的简单结果:
· C 5.2秒 gcc测试
· C++ 1分25秒 g++ test.cpp
· Zig 10.1秒 Zig build-exe test.zig
· Nim 45秒 nim c test.nim
· Rust 30分钟 rustc test.rs 后rust停止
· Swift 在30分钟的swiftc测试后停止
· D 6分钟后 segfault dmd test.d
· V 0.6秒 v test v

Zig不支持的所有事物但实际上非常有用:
· 没有类继承,不像C++,Java,Swift等。
· 通过Go之类的接口没有运行时多态性。
· 没有泛型。不能像在编译时检查的Swift中那样指定通用接口。
· 没有函数重载。不能多次使用不同的参数编写具有相同名称的函数。
· 没有异常抛出。
· 没有闭包。
· 没有垃圾收集。
· 没有用于资源获取的构造函数和析构函数是初始化(RAII)。

但是通过巧妙地使用一些核心功能,Zig能够提供几乎相同的功能:
· 类型可以在编译时像对象一样传递。
· 标签工会。在其他编程语言中也称为求和类型或变体。
· 函数指针、实数指针和指针算术。
· Zig代码可以在编译时部分评估。可以使用comptime关键字将代码标记为在编译时可执行。
· 函数和类型可以与结构相关联,但不能物理存储在结构中,因此C代码不可见。

成立基金会

2020年07月12日,Zig 语言官方自豪地宣布成立 Zig软件基金会(Zig Software Foundation,ZSF),这是一家非营利性公司,致力于促进、保护和推进 Zig 编程语言,支持并促进多元化和国际化的 Zig 开发者社区的发展,并向学生提供教育和指导,教导下一代程序员要有能力、有道德并互相遵循高标准。官方公告中还宣布任命 Loris Cro 为 ZSF 社区副总裁,他的职责是提高社区的接受度和参与度、寻求公司捐款,并找到公共研发资金和计划。同时 Zig 项目主页上也给出了基金会主页地址。官方表示 ZSF 保持开放式运营,也就是做一种开源业务,在 ZSF 主页上可以查看到财务详细信息、董事会会议记录以及所有常规记录。

综合来看,zig 作为一种新的现代编程语言,从命令行来看,整个设计十分简洁,易于使用。其核心设计之一是减少“魔法”:没有隐式控制流、没有隐式内存分配、没有预处理器,没有宏。

重要里程碑:已初步实现自举

Zig 在2022年8月下旬合并了名为 "make self-hosted the default compiler" 的 PR—— 意思就是默认编译器已实现自托管 (self-hosted)。在编程语言中,所谓自托管就是该编程语言实现的编译器可编译自己。因此该合并这个 PR 意味着 Zig 已初步实现自举。从 Zig 0.10.x 开始,开发者即可默认使用新的自托管编译器。当然,如果使用新编译器遇到问题,添加 -fstage1 flag 即可获取旧的编译器。开发团队表示,从现在到 0.10.0,他们将尽最大努力解决现实世界的错误,并尽可能多地让新编译器支持第三方项目。根据 Zig 维护者的介绍,Zig 自托管编译器有大概 20 万行代码,包含 LLVM、WASM、C、x86_64、arm 和 aarch64 后端。编译器代码库采用 Zig 而不是 C++ 编写,使用的内存显着减少,优化了性能。从 PR 的描述来看,目前许多功能仍处于待实现阶段。当然,初步实现自托管编译器只是 Zig 的里程碑之一,要发布 1.0 仍有许多重要工作完成:
完成自托管编译器。
稳定语言特性,不再有语言特性变更
完成语言规范初稿
实现官方包管理器
提供稳定标准库
在没有任何重大更改的情况下进行一个完整的发布周期
最后标记 1.0
由于合并此 PR 后会发生变化,为了确保项目正确地构建和运行,请查看这份指南

使用的感受:
1).用zig写出的代码更防崩,不会像C那样出现很多内存非法访问的情况(比如:栈保护、整数溢出、下标越界、OOM、DF、Leak等)
2).拥有“类”语法,相同功能的项目开发过程中,所需编写的总代码量肯定会比C少
3).比Rust/C++容易学,但是比C稍难学一点点,无需 FFI/bindings 的 C 库集成
4).基于LLVM开发的编译器,天生支持交叉编译多种CPU架构的目标代码
5).可与C、asm进行混合开发,兼容C/C++,完全可以拿来当另一个C编译器用
6).支持编译期代码,编译速度快,原生支持异步
7).可以源码中集成单元测试代码,支持 try catch exception处理
8).自带基于自身脚本的构建系统,不需要makefile之类的

缺点及风险:
1).小团队开发维护的编程语言,目前网上能看到的只有跟Uber一家有合作
2).未成熟和定型,三年前的zig项目,现在已经不能直接编译了(构建脚本已变)
3).小众,国内用的人更加少,文档不全,中文的资料更是少得可怜

最新版本:0.5
0.5.0 版本经过 6 个月打磨,包括了 1541 次 commit,带来了许多新的内容。跟随 LLVM 的步伐,前几天 LLVM 9 发布了,所以 Zig 目前升级到了 LLVM 9,不再兼容 LLVM 8。值得注意的是,这意味着 Zig 现在支持 RISC-V。同时 Zig 还可以以 Emscripten 为编译目标操作系统。Emscripten 是一个独特的 LLVM 后端,它可以将 LLVM 字节码编译成 JavaScript(asm.js),往上追溯其实也就是通过 Clang 将 C 和 C++ 代码编译成 JavaScript(asm.js),可以大大简化现有代码在 Web 时代的重用。除了 asm.js,Emscripten 还支持 WebAssembly 这一更加先进的 Web 技术,通过与 asm.js 类似的机制,Emscripten 可以生成 WASM 二进制字节码。引入了异步函数(async)。该功能不依赖于宿主操作系统,甚至不依赖于堆分配的内存,这意味着异步函数可以用于裸金属(freestanding)目标。自动推导函数是否为异步,并允许在非异步函数上进行 async/await,这意味着 Zig 库对阻塞与异步 I/O 是不可知的,避免了函数染色(function colors)。Zig 标准库实现了一个事件循环,将异步函数复用到线程池上,实现 M:N 并发。多线程安全和竞争检测是尚在积极研究的领域。完整更新内容查看发行公告

最新版本:0.6
Zig 0.6.0 已发布,此版本的开发周期历时 6 个月,有 122 名不同贡献者提交了 2527 次 commit。Zig 0.6.0 保持与 LLVM 一致的更新节奏,已将 LLVM 升级到最新版 LLVM 10,并且不再兼容 LLVM 9。新的 LLVM 版本主要是修复 Bug,尤其是对 ARM 支持、MIPS 支持、RISC-V 支持等方面的错误修复。这也是第一个将 Zig 的所有补丁合并到上游的 LLD 版本。因此,Zig 的源码库中不再包含 LLD 源码的 fork,也就意味着源码的 tarball zig-0.6.0.tar.xz 比 zig-0.5.0.tar.xz 小了 0.5MiB,因为删除 LLD 源码比这个发布周期中所有其他的改动加起来还节省了更多的空间。请注意,新的 Bootstrap Tarball 捆绑了 Zig 编译器的所有依赖项,其中包括 LLVM、LLD 和 Clang。详细内容查看发行公告

最新版本:0.9
Zig 0.9.1 已于2022年2月下旬发布,此版本的更新内容只有 bug 修复,不引入任何新特性和改进。修复的 bug 涉及到编译器、标准库、C 翻译、zig cc/zig c++ 以及语言参考。
修复函数 Handle typedef 的无效返回类型
使用科学计数法修复浮点常量的宏定义问题
修复翻译十六进制浮点常量的宏定义问题
使用anyopaque替代涉及c_void 的内容
添加有关隐式结构指针取消引用的文档
修复or示例中的优先级问题
……

虽然这是一个 Bugfix 版本,不过 0.9.1 仍存在部分已知但未解决的错误,包括编译方面的错误。按照发布计划,0.9.1 是 0.9.x 的最后一个版本。下一个主要版本 0.10.0 发布周期的主要目标则是稳定语言特性、创建语言规范的初稿和自托管编译器。下一个发布周期中部分即将到来的里程碑:
自托管编译器可以使用 LLVM 后端构建自身
所有行为测试和其他测试都通过 LLVM 后端。此时可以发布自托管编译器而不是 Bootstrap 编译器。
自托管编译器可以使用 C 后端构建自身
对 ELF 的自托管链接器支持
对 PE/COFF 的自托管链接器支持
通过 x86 后端或 aarch64 后端的行为测试,在针对相应架构时释放完整编译速度

以下是 Zig 达到 1.0 的要求:
完成自托管编译器。
稳定语言特性,不再有语言特性变更
完成语言规范初稿
实现官方包管理器
提供稳定标准库
在没有任何重大更改的情况下进行一个完整的发布周期,最后标记 1.0。

Zig 0.11.0 的开发周期长达8个月,于2023年8月上旬发布,共有 269 名贡献者提交了超过 4457 个 commit,最大的亮点莫过于软件包管理的首次亮相。开发团队表示,官方软件包管理器的首次亮相。虽然目前仍处于早期阶段,但已经足够成熟,可以在很多情况下使用。注意目前没有所谓的 “官方” 软件包仓库:软件包是任意的目录树,可以是本地目录,也可以是来自互联网的归档文件。软件包信息在名为 build.zig.zon 的文件中声明。ZON (Zig Object Notation) 是新引入的一种简单数据交换格式,它使用 Zig 的匿名结构体和数组初始化语法,以类似 JSON 等其他格式的方式声明对象。软件包的 build.zig.zon 文件应如下所示:
.{
    .name = "my_package_name",
    .version = "0.1.0",
    .dependencies = .{
        .dep_name = .{
            .url = "https://link.to/dependency.tar.gz",
            .hash = "xxx",
        },
    },
}


官方主页:https://ziglang.org/

中文主页:https://ziglang.org/zh/