0.14 版本更新介绍

https://ziglang.org/download/0.14.0/release-notes.html

发布概览

Zig 0.14.0 版本是经过 9 个月的工作,由 251 位不同的贡献者 完成,包含 3467 个提交 的成果。该版本专注于提升 健壮性最优性可重用性,并通过 Zig 软件基金会 (Zig Software Foundation) 资助开发。

核心主题与重要更新

  1. 提升编译速度与开发效率:
  • 版本说明强调了两个重要的长期投资:增量编译 (Incremental Compilation)快速 x86 后端 (fast x86 Backend)
  • 这两项投资的核心目标是 “reducing edit/compile/debug cycle latency”(减少编辑/编译/调试循环延迟)。
  • 增量编译 功能在本次发布中可以通过 -fincremental 标志选择启用,但目前尚未完全成熟。它在配合文件系统监控时表现良好,尤其是在仅检查编译错误时能显著提升反馈速度。
  • 引用:$ zig build -Dno-bin -fincremental –watch (展示了在大型代码库中快速获得编译错误反馈的示例)。
  • 目前不兼容 usingnamespace,建议用户尽量避免使用。
  • x86 后端 在行为测试中表现出色,已经通过了 98% 的测试用例,编译速度显着快于 LLVM 后端,并且支持更好的调试器。它有望在下一个发布周期成为调试模式下的默认后端。可以通过 -fno-llvm 或 use_llvm = false 启用。
  • 引用:The x86 backend is now passing 1884/1923 (98%) of the behavior test suite compared to the LLVM backend.
  1. 增强目标平台支持 (Target Support):
  • 这是一个主要主题,Zig 的跨平台编译能力得到了极大扩展。
  • 版本说明详细列出了不同目标平台在语言特性、标准库、代码生成、链接器、调试信息、libc 和 CI 测试等方面的支持级别,采用分层系统 (Tier System) 进行分类(Tier 1 为最高级别)。
  • 引用:A major theme in this Zig release is improved target support; the list of targets that Zig can correctly cross-compile to and run on has been greatly expanded.
  • 对 arm/thumb, mips/mips64, powerpc/powerpc64, riscv32/riscv64, 或 s390x 等目标平台的工具链问题、标准库支持缺失和崩溃问题有了显著改进。
  • 更新了目标三元组 (Target Triple Changes) 的命名和支持,以更准确地反映不同平台(如 Windows、Linux 使用 musl libc)的 ABI 和特性。
  1. 重要的语言特性变化 (Language Changes):
  • 标记 switch (Labeled Switch): 允许 switch 语句拥有标签并被 continue 语句指向,从而实现更清晰的状态机实现。关键动机在于生成优化后的代码,特别是通过不同的分支指令帮助 CPU 进行更准确的分支预测,提升性能。
  • 引用:Zig 0.14.0 implements an accepted proposal which allows switch statements to be labeled, and to be targeted by continue statements.
  • 引用:This language construct is designed to generate code which aids the CPU in predicting branches between cases of the switch, allowing for increased performance in hot loops…
  • 更新 Zig 的 tokenizer 利用此特性带来了 13% 的性能提升。
  • 声明字面量 (Decl Literals): 扩展了 .foo 语法,不仅可以引用枚举成员,还可以引用目标类型上的任何声明 (const/var/fn)。这使得初始化结构体字段和调用初始化函数更加简洁和安全,特别是避免了无效的默认字段值问题。
  • 引用:Zig 0.14.0 extends the “enum literal” syntax (.foo) to provide a new feature, known as “decl literals”.
  • 许多现有使用字段默认值的地方可能更适合使用 default 或 empty 等声明来处理,以确保数据不变性。
  • 字段和声明不能共享名称 (Fields and Declarations Cannot Share Names): 引入了容器类型(struct, union, enum, opaque)的字段和声明不能同名的限制,解决了歧义问题并便于文档生成。
  • 引用:Zig 0.14.0 introduces a restriction that container types (struct , union , enum and opaque ) cannot have fields and declarations ( const / var / fn ) with the same names.
  • @splat 支持数组 (@splat Supports Arrays): 扩展了 @splat 内置函数,使其可以应用于数组和哨兵终止数组,方便用常量值初始化数组。
  • 引用:Zig 0.14.0 expands the @splat builtin to apply not only to vectors, but to arrays.
  • 全局变量可以相互引用地址 (Global Variables can be Initialized with Address of Each Other): 允许全局变量在初始化时相互引用地址。
  • @export 操作数现在是指针 (@export Operand is Now a Pointer): @export 内置函数现在接受一个指针作为操作数,使其用法更清晰和一致,通常只需在旧用法前添加 &。
  • 新的 @branchHint 内置函数,取代 @setCold (New @branchHint Builtin, Replacing @setCold): 引入 @branchHint 内置函数,允许开发者向优化器提示分支的可能性,有 .none, .likely, .unlikely, .cold, .unpredictable 等选项。这取代了旧的 @setCold。
  • 引用:Zig 0.14.0 introduces a mechanism to communicate this information: the new @branchHint(comptime hint: std.builtin.BranchHint) builtin.
  • @branchHint 必须是其所在块或函数的第一个语句。
  • 移除 @fenceStoreLoad Barriers: 移除 @fence(.StoreLoad),其功能现在可以通过使用 SeqCst 或 Acquire/Release 原子操作来实现。
  • Packed Struct Equality 和 Packed Struct Atomics: 允许直接对 Packed Struct 进行相等性比较和原子操作,不再需要 @bitCast 到底层整数类型。
  • @ptrCast 允许改变切片长度 (@ptrCast Allows Changing Slice Length): #22706
  • 移除匿名结构体类型,统一元组 (Remove Anonymous Struct Types, Unify Tuples): 重构匿名结构体字面量和元组的工作方式,使其使用“普通”结构体类型和基于 AST 节点及结构体的等价性。
  • Calling Convention 增强和 @setAlignStack 被取代 (Calling Convention Enhancements and @setAlignStack Replaced): std.builtin.CallingConvention 现在是一个标记联合,包含更多目标平台特定的调用约定,并允许通过 CommonOptions 设置栈对齐等选项。.c 调用约定现在是一个声明,可以通过 callconv(.c) 使用 Decl Literals 访问。@setAlignStack 被移除,其功能现在通过调用约定的选项实现。
  • *std.builtin.Type 字段重命名和简化 (std.builtin.Type Fields Renamed and Simplify Usage Of ?const anyopaque): std.builtin.Type 联合体的字段名称改为小写,并增加了对 default_value_ptr 和 sentinel_ptr 字段的 helper 方法,以简化使用。
  • 不允许非标量哨兵类型 (Non-Scalar Sentinel Types Disallowed): 哨兵值现在只能是支持 == 操作符的标量类型。
  • @FieldType 内置函数 (@FieldType builtin): 新增 @FieldType 内置函数,用于获取给定类型和字段名称的字段类型,取代了 std.meta.FieldType 函数。
  • @src 获得 Module 字段 (@src Gains Module Field): std.builtin.SourceLocation 结构体新增 module 字段。
  • @memcpy 规则调整 ( @memcpy Rules Adjusted): langspec 定义调整,源和目标元素类型必须内存可强制转换,从而确保是原始复制操作。对 comptime @memcpy 增加了别名检查和更高效的实现。
  • 禁止不安全的内存强制转换 (Unsafe In-Memory Coercions Disallowed): #22243
  • callconv, align, addrspace, linksection 不能引用函数参数 (#22264callconv, align, addrspace, linksection Cannot Reference Function Arguments): #22264
  • 函数调用的分支配额规则调整 (Branch Quota Rules Adjusted for Function Calls): #22414
  1. 标准库改进 (Standard Library):
  • DebugAllocator 和 SmpAllocator: 重写了 GeneralPurposeAllocator 并更名为 DebugAllocator,提高了调试模式下的性能。新增了 SmpAllocator,一个针对 ReleaseFast 模式和多线程优化的单例分配器,性能可与 glibc 媲美。
  • Allocator API 变化 (remap): std.mem.Allocator.VTable 新增了 remap 函数,允许在可能的情况下进行无需 memcpy 的内存重映射。resize 语义不变。Allocator.VTable 函数现在使用 std.mem.Alignment 类型。
  • ZON 解析和序列化 (ZON Parsing and Serialization): std.zon.parse 提供运行时解析 ZON 到 Zig 结构体的功能,std.zon.stringify 提供运行时序列化功能。
  • 运行时页面大小 (Runtime Page Size): 移除了编译时已知的 std.mem.page_size,代之以编译时已知的 std.heap.page_size_min 和 std.heap.page_size_max。std.heap.pageSize() 提供运行时实际页面大小。解决了在 Apple 新硬件上运行 Linux 的支持问题。
  • Panic 接口 (#22594Panic Interface): #22594
  • Transport Layer Security (std.crypto.tls): #21872
  • process.Child.collectOutput API 变化 (#21872process.Child.collectOutput API Changed): API 签名改变,现在将 allocator 作为第一个参数传入。
  • LLVM Builder API: LLVM bitcode builder API 移至 std.zig.llvm,方便第三方项目复用。
  • 拥抱“非管理”风格容器 (Embracing “Unmanaged”-Style Containers): 大部分带有内置 allocator 的标准库容器(如 std.ArrayList, std.ArrayHashMap)已被弃用,推荐使用“非管理”风格容器(如 std.ArrayListUnmanaged, std.ArrayHashMapUnmanaged),并在需要时显式传递 allocator。
  • std.c 重组 (std.c Reorganization): 重组了 std.c,使其结构更清晰,并改变了对不存在符号的处理方式(从 @compileError 改为 void 或 {}),移除了标准库中最后一个 usingnamespace 的使用点。
  • 弃用列表 (List of Deprecations): 列出了大量被弃用或重命名的标准库函数和类型。
  • Binary Search: #20927
  • std.hash_map 获得 rehash 方法 (#20927std.hash_map gains a rehash method): 为解决 HashMap 移除元素后的性能下降问题新增了 rehash 方法(Array Hash Map 没有此问题)。此方法预计在未来不再需要时会被删除。
  1. 构建系统升级 (Build System):
  • 基于现有模块创建 Artifacts (Creating Artifacts from Existing Modules): 修改了构建系统 API,允许从现有的 std.Build.Module 对象创建 Compile 步骤,使模块图的定义更清晰,组件复用更容易。旧的 API 用法已被弃用。
  • 引用:Zig 0.14.0 modifies the build system APIs for creating Compile steps, allowing them to be created from existing std.Build.Module objects.
  • 允许包按名称暴露任意 LazyPath (Allow Packages to Expose Arbitrary LazyPaths by Name): 新增 std.Build.addNamedLazyPath 和 std.Build.Module.namedLazyPath 方法,允许依赖包按名称暴露生成的 LazyPath 给其依赖者使用。
  • addLibrary 函数: 新增 addLibrary 函数,取代 addSharedLibrary 和 addStaticLibrary,使得在 build.zig 中更容易切换链接模式,并与 linkLibrary 名称更匹配。
  • 文件系统监控 (File System Watching): #22105
  • 新包哈希格式 (New Package Hash Format): 引入新的包哈希格式,包含 32 位 id 和 32 位校验和,用于在去中心化生态系统中唯一标识包。当 fork 项目时,如果上游仍维护,应该重新生成 fingerprint。
  • WriteFile Step, RemoveDir Step, Fmt Step: 新增或改进了这些构建步骤。
  • Breakings: 多个 installHeader 和 installHeadersDirectory 相关函数签名改变,现在接受 LazyPath。生成的 -femit-h 头文件不再默认发出。
  1. 编译器和链接器改进 (Compiler and Linker):
  • 多线程后端支持 (Multithreaded Backend Support): 部分编译器后端(如 x86 后端)支持在单独线程中运行代码生成,显著提升了编译速度。
  • LLVM 19: 升级到 LLVM 19.1.7。
  • 链接器输入文件解析移至前端 (Move Input File Parsing to the Frontend): 将 GNU ld 脚本处理移至前端,以便在编译开始时了解所有链接器输入,实现编译和链接同时进行。为了避免对所有 .so 文件进行文件系统访问,新增了 -fallow-so-scripts 命令行标志,允许用户选择启用对 .so 脚本的支持。
  • 引用:Moves GNU ld script processing to the frontend to join the relevant library lookup logic, making the libraries within subject to the same search criteria as all the other libraries.
  1. 集成模糊测试器 (Fuzzer):
  • Zig 0.14.0 集成了一个模糊测试器,目前处于 alpha 状态。通过 –fuzz 命令行选项启用。
  • 可以针对包含 std.testing.fuzz 的单元测试二进制文件进行进程内模糊测试。
  • 提供了 Web UI (http://127.0.0.1:38239/) 显示实时代码覆盖率。
  1. Bug 修复与 Toolchain 更新 (Bug Fixes and Toolchain):
  • 关闭了 416 个 bug 报告。但版本说明坦承 “This Release Contains Bugs”,并指出在 1.0.0 版本达到 Tier 1 支持时会增加 bug 策略。
  • UBSan Runtime: Debug 模式下默认启用 UBSan 运行时库,为 C 代码的未定义行为提供更详细的恐慌信息和堆栈跟踪。可以通过 -fno-ubsan-rt 和 -fubsan-rt 控制。
  • compiler_rt: 包含了优化的 memcpy 实现。
  • musl 1.2.5: 捆绑的 musl 更新并应用了 CVE 修复和目标平台特定补丁。不再捆绑 musl 的 memcpy 文件,而是使用 Zig 的优化实现。
  • glibc 2.41: 支持 glibc 2.40 和 2.41 交叉编译,修复了多个与 glibc 相关的问题。
  • Linux 6.13.4 Headers, Darwin libSystem 15.1, MinGW-w64, wasi-libc: 更新了捆绑的操作系统头文件和 libc 版本。捆绑了 winpthreads 库。

社区贡献与资助:

  • 版本说明详细感谢了 251 位 为本次发布做出贡献的开发者。
  • 特别感谢了通过经常性捐赠支持 Zig 的个人和组织赞助商,强调了开源社区驱动的重要性。

路线图展望:

  • 版本说明中穿插提到了未来的一些计划,例如提升增量编译的成熟度、使 x86 后端成为调试模式下的默认选项、改进 ZON 导入,以及未来容器和哈希地图的改进。
  • 最终目标是达到 1.0.0 版本,届时 Tier 1 支持将包含 bug 政策。

关于 Zig 0.14.0 版本的常见问题解答

Zig 0.14.0 版本的主要更新和亮点是什么?

Zig 0.14.0 版本是长达 9 个月开发工作和 3467 次提交的成果,主要亮点包括:显著增强了对多种目标平台的支持,包括 arm/thumb、mips/mips64、powerpc/powerpc64、riscv32/riscv64 和 s390x 等,许多之前存在工具链问题、标准库支持缺失或崩溃的情况现在应该可以正常工作了。此外,该版本在构建系统方面进行了大量升级,并对语言进行了多项重要改进,例如引入了 Labeled Switch 和 Decl Literals 等新特性。为了缩短编辑/编译/调试周期,版本还迈向了两个长期投资目标:增量编译和快速 x86 后端。

Zig 如何对不同目标平台的开发支持进行分级?

Zig 使用四层系统来对不同目标平台的支持级别进行分类,其中 Tier 1 是最高级别:

  • Tier 1: 所有非实验性语言特性都能正常工作。编译器能够独立生成目标平台的机器代码,功能与 LLVM 相当。即使在交叉编译时,该目标平台也有可用的 libc。
  • Tier 2: 标准库的跨平台抽象在该目标平台上有实现。该目标平台具备调试信息能力,可以在断言失败和崩溃时生成堆栈跟踪。CI 机器在每次 master 分支提交时都会自动构建和测试该目标平台。
  • Tier 3: 编译器可以依赖外部后端(如 LLVM)为该目标平台生成机器代码。链接器可以为该目标平台生成目标文件、库和可执行文件。
  • Tier 4: 编译器可以依赖外部后端(如 LLVM)为该目标平台生成汇编源代码。如果 LLVM 将此目标平台视为实验性,则需要从源代码构建 LLVM 和 Zig 才能使用它。

什么是 Labeled Switch,它有什么优势?

Labeled Switch 是 Zig 0.14.0 中引入的一项语言特性,允许 switch 语句被标记,并作为 continue 语句的目标。continue :label value 语句会用 value 替换原始的 switch 表达式操作数,并重新评估 switch。尽管在语义上类似于循环中的 switch,但 Labeled Switch 的关键优势在于其代码生成特性。它可以生成帮助 CPU 更准确预测分支的代码,从而提高热循环中的性能,特别是在处理指令分派、评估有限状态自动机 (FSA) 或执行类似基于 case 的评估时。这有助于 branch predictor 更准确地预测控制流。

Decl Literals 是什么,它解决了哪些问题?

Decl Literals 是 Zig 0.14.0 扩展 “enum literal” 语法 (.foo) 而引入的新特性。现在,一个枚举字面量 .foo 不仅可以引用枚举变体,还可以使用 Result Location Semantics 引用目标类型上的任何声明(const/var/fn)。这在初始化结构体字段时特别有用,可以避免重复指定类型,并有助于避免 Faulty Default Field Values 的问题,确保数据不变量不会因覆盖单个字段而受到破坏。它也支持直接调用函数来初始化值。

Zig 0.14.0 版本在内存分配器方面有哪些值得关注的变化?

该版本对内存分配器进行了多项改进:

  • DebugAllocator: GeneralPurposeAllocator 已被重写并更名为 DebugAllocator,以解决其依赖于编译时已知的页面大小的问题,并提升性能。
  • SmpAllocator: 引入了一个新的分配器,专为 ReleaseFast 优化模式和多线程环境设计。它是一个单例,使用全局状态,每个线程拥有独立的空闲列表,并通过原子操作处理线程资源回收,即使线程退出也能恢复数据。其性能与 glibc 相当。
  • Allocator API Changes (remap): std.mem.Allocator.VTable 引入了一个新的 remap 函数,允许尝试扩展或收缩内存并可能重新定位,如果无法在不执行内部 memcpy 的情况下完成,则返回 null,提示调用者自行处理复制。同时,resize 函数保持不变。Allocator.VTable 中的所有函数现在使用 std.mem.Alignment 类型代替 u8,增加了类型安全。
  • Runtime Page Size: 移除了编译时已知的 std.mem.page_size,代之以编译时已知的页面大小上下界 std.heap.page_size_min 和 std.heap.page_size_max。运行时获取页面大小可以使用 std.heap.pageSize(),它会优先使用编译时已知的值,否则在运行时查询操作系统并缓存结果。这修复了对 Asahi Linux 等新硬件上运行 Linux 的支持。

Zig 0.14.0 版本如何改进构建系统,特别是处理模块和依赖关系?

Zig 0.14.0 版本在构建系统方面有多项重要改进:

  • Creating Artifacts from Existing Modules: 修改了构建系统 API,允许从现有的 std.Build.Module 对象创建 Compile 步骤。这使得模块图的定义更加清晰,并且可以更容易地重用图中的组件。
  • Allow Packages to Expose Arbitrary LazyPaths by Name: 引入了 std.Build.Step.addNamedLazyPath 方法,允许包暴露命名的 LazyPath,例如生成的源代码文件,供依赖包使用。
  • New Package Hash Format: 引入了新的包哈希格式,包括 name、version 和 fingerprint 字段。fingerprint 是一个重要的概念,用于全局唯一标识包,即使在去中心化的生态系统中也能准确识别更新版本。
  • addLibrary Function: 引入 addLibrary 函数作为 addSharedLibrary 和 addStaticLibrary 的替代,允许在 build.zig 中更容易地切换链接模式,并与 linkLibrary 函数名称保持一致。
  • Import ZON: ZON 文件现在可以在编译时通过 @import(“foo.zon”) 导入,前提是结果类型已知。

Zig 0.14.0 版本在编译器后端和编译速度方面有哪些进展?

该版本在编译器后端和编译速度方面取得了进展:

  • Multithreaded Backend Support: 部分编译器后端,如 x86 Backend,现在支持在单独的线程中运行代码生成,这显著提高了编译速度。
  • Incremental Compilation: 引入了增量编译特性,可以通过 -fincremental 标志启用。尽管尚未默认启用,但结合文件系统监听,可以显著缩短修改代码后的重新分析时间,提供快速的编译错误反馈。
  • x86 Backend: x86 后端在行为测试套件中的通过率已接近 LLVM 后端,并且在开发时通常比 LLVM 后端提供更快的编译速度和更好的调试器支持。虽然尚未默认选中,但鼓励用户尝试使用 -fno-llvm 或在构建脚本中设置 use_llvm = false 来启用。

Zig 0.14.0 版本在工具链和运行时方面有哪些值得注意的更新?

该版本在工具链和运行时方面也有多项更新:

  • UBSan Runtime: Zig 现在为 UBSan 提供了运行时库,在 Debug 模式下默认启用,可以在 C 代码触发未定义行为时提供详细的错误信息和堆栈跟踪。
  • LLVM 19: Zig 已升级到 LLVM 19.1.7 版本。
  • musl 1.2.5: 更新了捆绑的 musl 版本,并应用了安全补丁和目标平台特定补丁。
  • glibc 2.41: 支持 cross-compiling glibc 2.40 和 2.41 版本,并修复了多个问题,提高了与 glibc 的兼容性。
  • Linux 6.13.4 Headers: 包含了 Linux 内核头文件版本 6.13.4。
  • Darwin libSystem 15.1: 包含了 Xcode SDK 版本 15.1 的 Darwin libSystem 符号。
  • MinGW-w64: 更新了捆绑的 MinGW-w64 版本,并捆绑了 winpthreads 库,支持 cross-compiling 到 thumb-windows-gnu。
  • wasi-libc: 更新了捆绑的 wasi-libc 版本。
  • Optimized memcpy: 提供了优化的 memcpy 实现,不再捆绑 musl 的 memcpy 文件。
  • Integrated Fuzzer: 集成了 alpha 质量的 fuzzer,可以通过 –fuzz CLI 选项使用,并提供一个 fuzzer Web UI 显示实时代码覆盖率。

总结

Zig 0.14.0 版本是向 1.0.0 版本迈进的重要一步,在性能优化(尤其是编译速度)、跨平台支持、语言特性和标准库方面都带来了显著改进。增量编译和快速 x86 后端是关键的长期投资,旨在提升开发者体验。新语言特性如 Labeled Switch 和 Decl Literals 提供了更强大和安全的编程模式。标准库的重组和容器的调整反映了社区的使用模式和最佳实践。构建系统也获得了重要升级,使模块管理和依赖处理更加灵活。尽管仍存在已知 bug,但 Zig 社区在本次发布中展示了活跃的开发和持续的进步。