0.14 版本更新介绍
https://ziglang.org/download/0.14.0/release-notes.html
发布概览
Zig 0.14.0 版本是经过 9 个月的工作,由 251 位不同的贡献者 完成,包含 3467 个提交 的成果。该版本专注于提升 健壮性、最优性 和 可重用性,并通过 Zig 软件基金会 (Zig Software Foundation) 资助开发。
核心主题与重要更新
- 提升编译速度与开发效率:
- 版本说明强调了两个重要的长期投资:增量编译 (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.
- 增强目标平台支持 (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 和特性。
- 重要的语言特性变化 (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
- 标准库改进 (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 没有此问题)。此方法预计在未来不再需要时会被删除。
- 构建系统升级 (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 头文件不再默认发出。
- 编译器和链接器改进 (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.
- 集成模糊测试器 (Fuzzer):
- Zig 0.14.0 集成了一个模糊测试器,目前处于 alpha 状态。通过 –fuzz 命令行选项启用。
- 可以针对包含 std.testing.fuzz 的单元测试二进制文件进行进程内模糊测试。
- 提供了 Web UI (http://127.0.0.1:38239/) 显示实时代码覆盖率。
- 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 社区在本次发布中展示了活跃的开发和持续的进步。