From 9f5595f8deed5543cccf0ada7fa80525ab92e00d Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Wed, 22 Oct 2025 09:42:08 +0800 Subject: [PATCH 01/11] =?UTF-8?q?docs(course):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E8=AF=BE=E7=A8=8B=E6=96=87=E6=A1=A3=E4=B8=AD=E7=9A=84=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E5=9B=BE=E7=89=87=E8=B7=AF=E5=BE=84=E5=92=8C=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 `index.md` 和 `prologue.md` 中的封面图片路径,确保正确引用 - 在侧边栏中新增 Zig 0.15.1 升级指南和版本说明链接 - 更新 `0.15.1-description.md` 中的版本发布日期和相关内容,确保信息准确性 --- course/.vitepress/sidebar.ts | 8 + course/index.md | 2 +- course/prologue.md | 2 +- course/update/0.15.1-description.md | 1021 +++++++++++++++++++++++---- 4 files changed, 892 insertions(+), 141 deletions(-) diff --git a/course/.vitepress/sidebar.ts b/course/.vitepress/sidebar.ts index 88d465f5..ec234449 100644 --- a/course/.vitepress/sidebar.ts +++ b/course/.vitepress/sidebar.ts @@ -216,6 +216,14 @@ export default [ text: "版本说明", collapsed: true, items: [ + { + text: "0.15.1 升级指南", + link: "/update/upgrade-0.15.1", + }, + { + text: "0.15.1 版本说明", + link: "/update/0.15.1-description", + }, { text: "0.14.0 升级指南", link: "/update/upgrade-0.14.0", diff --git a/course/index.md b/course/index.md index c1aacba8..852cdbb1 100644 --- a/course/index.md +++ b/course/index.md @@ -20,7 +20,7 @@ showVersion: false > 允许函数处理各种数据,以及一小组新的编译器指令,以允许使用反射访问有关这些类型的信息。 > Zig 还旨在提高代码的安全性,它不提供垃圾回收(GC),但是使用可选类型代替 `null` ,这避免了空指针的出现。 -![Cover Image](./public/cover_image.png "Cover Image") +![Cover Image](/cover_image.png "Cover Image") ## 为何使用 Zig diff --git a/course/prologue.md b/course/prologue.md index 08e40473..323695fe 100644 --- a/course/prologue.md +++ b/course/prologue.md @@ -28,4 +28,4 @@ C 很好,非常好,它非常成功,以至于 C 现在已经不再是一门 Zig 的社区需要更多的人来构建,所以我写了这个文档,帮助新人来更好的理解和学习 Zig! -![Cover Image](./public/cover_image.png "Cover Image") +![Cover Image](/cover_image.png "Cover Image") diff --git a/course/update/0.15.1-description.md b/course/update/0.15.1-description.md index 8f9a3835..37d9db3d 100644 --- a/course/update/0.15.1-description.md +++ b/course/update/0.15.1-description.md @@ -6,239 +6,982 @@ showVersion: false # `0.15.1` -2025/1/20,`0.15.1` 发布,自上一个版本来历时 5 个月,共有 162 位贡献者参与,进行了 647 次提交! +2025/8/30,`0.15.1` 发布,自上一个版本来历时 5 个月,共有 162 位贡献者参与,进行了 647 次提交! -Zig 官方团队直接从 `0.14.0` 跨越到了 `0.15.1`。在默认选择 Zig 的 x86 后端后,调试编译速度提高了 **5** 倍;正在开发的 aarch64 后端也紧随其后。同时,随着 `Writergate` 事件以及一系列语言变更和标准库的调整,**大量** API 有破坏性更改;这预示着 async/await 即将重生;也是语言稳定化的最后关头。 +> 本次更新,Zig 团队直接从 `0.14.1` 跨越到了 `0.15.1`。 -## 编译器重大改进 +Zig 目前默认使用 x86 后端,调试编译速度提高了 **5** 倍;aarch64 后端正在开发中。 -### x86 后端性能突破 +目前 Zig 已经进入了语言稳定的最后关头,本次更新带来了部分 break change,同时一直等待的 async 特性也有苗头了。 -**调试模式**的编译速度提高了 **5** 倍;这是本次更新最显著的性能改进。x86 后端现在是**调试模式**的默认选择,为缩短开发周期提供了强有力的支持。 +## 系统最低版本要求 -### aarch64 后端开发进展 +| 操作系统(Operating System) | 最低版本要求 (Minimum Version) | +| :------------------------- | :-----------------------------: | +| Dragonfly BSD | 6.0 | +| FreeBSD | 14.0 | +| Linux | 5.10 | +| NetBSD | 10.1 | +| OpenBSD | 7.6 | +| macOS | 13.0 | +| Solaris | 11 | +| Windows | 10 | -aarch64 后端的开发也在稳步推进,持续优化 ARM 平台支持,为多架构开发提供更好的体验。 +## 语言变动 -### 增量编译功能增强 +小改动: -增量编译功能得到了进一步优化,现在支持: +packed union 字段现在不允许再单独指定 align 属性,这与 packed struct 的现有行为保持一致。此前即使为字段强制指定了对齐方式,也不会实际影响字段的对齐,这次迁移只需删去该部分即可。[#22997](https://github.com/ziglang/zig/pull/22997) -- 与文件系统监视结合使用 -- 仅检查编译错误模式 -- 更快的重建速度 +### 移除 `usingnamespace` -### 多线程代码生成 +该关键字增加了 `declaration` 的“预期”定义位置与“实际”定义位置之间的距离。在没有它的情况下,查找某个 `declaration` 的定义非常简单:只需找到你要查找的命名空间的定义,再在该类型 `declaration` 内部找到具体标识符即可。然而,有了 usingnamespace,程序员常常会被迫在不同类型和文件之间反复查找,寻觅声明的真实 `declaration` 位置。 -编译器现已支持多线程代码生成,能够充分利用多核 CPU 的性能优势。 +这不仅影响了人类的可读性,对于工具链来说也带来了问题;例如,`Autodoc` 在遇到复杂的 `usingnamespace` 用法时无法准确分析(可以试着在 `0.14.1` 文档中查找 `std.c` 下的 `dl_iterate_phdr`)。 -## 语言特性重大调整 +移除该特性后,所有标识符的来源都可以被轻松追溯到其实际导入的位置。 -### 移除 `usingnamespace` 关键字 +此外,`usingnamespace` 还会导致糟糕的命名空间划分。当声明被存放在单独的文件中时,通常意味着这些声明彼此之间有某些共同点,而与其他文件的内容无关。因此,将这些声明通过单独的命名空间进行暴露,而不是混入一个更通用的父级命名空间,其实是更合理的选择。简而言之:**好的命名空间结构是非常有价值的**。 -这是一个**破坏性变更**,`usingnamespace` 关键字被完全移除。所有使用 `usingnamespace` 的代码都需要重写,建议采用显式导入方式替代,以提高代码可读性和可维护性。 +最后,移除该特性让增量编译的实现变得根本上更加简单。 -### 移除异步关键字 +#### 示例:条件包含 + +`usingnamespace` 以前常用于有条件地包含声明,比如如下写法: + +```zig +pub usingnamespace if (have_foo) struct { + pub const foo = 123; +} else struct {}; +``` + +现在的替代办法其实非常简单:通常可以直接无条件地声明即可。Zig 拥有惰性编译特性,未被引用的声明不会被分析,因此这样不会有问题! + +```zig +pub const foo = 123; +``` -`async` 和 `await` 关键字已被移除,当前异步编程模型也随之取消,为未来新的异步方案做准备。这预示着 Zig 协程 的重生。 +但有时这种做法并不安全。例如,`foo` 虽然总能分析通过,但只有在 `have_foo` 为 true 时才有实际意义,在其他情况下引用则属于 bug。这种场景下,可以让未支持时声明直接在编译时报错: + +```zig +pub const foo = if (have_foo) + 123 +else + @compileError("foo not supported on this target"); +``` + +不过,这样做会破坏用 `@hasDecl` 进行特性检测的能力。如果确实需要特性检测,更推荐的写法是:将声明条件初始化为某种可检测的“哨兵值”(sentinel value),这样既不容易写错,也方便维护。常用的方案是使用 void 类型的 `{}`: + +```zig feature-detection.zig +const something = struct { + // 本例中,foo 被支持,bar 不被支持。 + const have_foo = true; + const have_bar = false; + pub const foo = if (have_foo) 123 else {}; + pub const bar = if (have_bar) undefined else {}; +}; + +test "use foo if supported" { + if (@TypeOf(something.foo) == void) return error.SkipZigTest; // 不支持 + try expect(something.foo == 123); +} + +test "use bar if supported" { + if (@TypeOf(something.bar) == void) return error.SkipZigTest; // 不支持 + try expect(something.bar == 456); +} + +const expect = @import("std").testing.expect; +``` + +#### 用例示例:多实现切换(Implementation Switching) -### 增强对非穷尽枚举的 `switch` 支持 +`usingnamespace` 的另一个常见用途是根据不同条件(如平台目标)在编译期选择不同的实现体,如: -新增对非穷尽枚举使用 `switch` 语句的支持,提高了枚举处理的灵活性,能够更好地处理未知枚举值,增强代码的健壮性。 +```zig +pub usingnamespace switch (target) { + .windows => struct { + pub const target_name = "windows"; + pub fn init() T { + // ... + } + }, + else => struct { + pub const target_name = "something good"; + pub fn init() T { + // ... + } + }, +}; +``` + +替代方式其实更简单,推荐直接将定义本身写成条件表达式: + +```zig +pub const target_name = switch (target) { + .windows => "windows", + else => "something good", +}; +pub const init = switch (target) { + .windows => initWindows, + else => initOther, +}; +fn initWindows() T { + // ... +} +fn initOther() T { + // ... +} +``` + +#### 用例示例:Mixins 混入 + +使用 `usingnamespace` 的另一个常见场景,是实现“混入”特性(mixins): + +```zig +/// 提供操作 `count` 字段方法的混入体。 +pub fn CounterMixin(comptime T: type) type { + return struct { + pub fn incrementCounter(x: *T) void { + x.count += 1; + } + pub fn resetCounter(x: *T) void { + x.count = 0; + } + }; +} + +pub const Foo = struct { + count: u32 = 0, + pub usingnamespace CounterMixin(Foo); +}; +``` + +替代写法基于前述的命名空间思想:其实**恰当的命名空间划分是好事**。Mixins 也可以用命名空间来组织。比如把 `incrementCounter` 和 `resetCounter` 名称中的 “counter” 前缀直接变成一个嵌套字段,这样 `foo.counter.increment()` 替代 `foo.incrementCounter()`,语义更直观。 + +实现方式可利用零位字段(zero-bit field)和 `@fieldParentPtr`: + +```zig +/// 提供操作 `count` 字段方法的混入体。 +pub fn CounterMixin(comptime T: type) type { + return struct { + pub fn increment(m: *@This()) void { + const x: *T = @alignCast(@fieldParentPtr("counter", m)); + x.count += 1; + } + pub fn reset(m: *@This()) void { + const x: *T = @alignCast(@fieldParentPtr("counter", m)); + x.count = 0; + } + }; +} + +pub const Foo = struct { + count: u32 = 0, + counter: CounterMixin(Foo) = .{}, +}; +``` + +这样写后,调用就是 `foo.counter.increment()`,不再是 `foo.incrementCounter()`。通过零位字段成功将 mixin “命名空间化”。 + +实际上,这比以前更灵活,也能让混入体包含如字段等更多内容。比如本例中甚至可以将 `count` 字段本身都移到 `CounterMixin` 内部(虽然这种场景下其实不算 mixin,因为只剩状态本身)。 + +更重要的是,如果某些复杂混入需要额外状态,这种办法也能避免每个站点都重复字段声明,维护起来更方便。 + + +### 移除 async 和 await 关键字 + +`async` 和 `await` 关键字已被移除,`@frameSize` 也已删除。 + +虽然 `suspend`、`resume` 及协程底层机制是否保留还需依据“无栈协程原语提案”进一步决定,但可以确认,Zig 语言层面将不再有 `async/await` 这样的关键字。 + +未来异步相关能力将仅以标准库的一部分(比如 Io 接口)存在。 + +### 非穷尽枚举的 switch 改进 + +现在,针对非穷尽(non-exhaustive)枚举使用 `switch` 时,可以将显式枚举标签与 `_` 分支(代表所有未命名值)组合: + +```zig +switch (enum_val) { + .special_case_1 => foo(), + .special_case_2 => bar(), + _, .special_case_3 => baz(), +} +``` + +此外,`switch` 语句现在也允许同时使用 `else` 和 `_`: + +```zig +const Enum = enum(u32) { + A = 1, + B = 2, + C = 44, + _ +}; + +fn someOtherFunction(value: Enum) void { + // 这样写会编译报错:“error: else and '_' prong in switch expression” + switch (value) { + .A => {}, + .C => {}, + else => {}, // 此处处理已命名但未列出的标签(这里就是 .B) + _ => {}, // 此处处理未命名标签 + } +} +``` + +### 布尔向量支持更多运算符 + +布尔向量现在支持按位非、按位与、按位或、按位异或,以及布尔非等运算。 + +### 内联汇编:类型化的 clobber 描述符 + +此前,clobber 描述符仅支持字符串类型。现在它们变为类型化的,实际用起来会更直观。 + +```zig +pub fn syscall1(number: usize, arg1: usize) usize { + return asm volatile ("syscall" + : [ret] "={rax}" (-> usize), + : [number] "{rax}" (number), + [arg1] "{rdi}" (arg1), + : "rcx", "r11" + ); +} +``` + +⬇️ + +```zig +pub fn syscall1(number: usize, arg1: usize) usize { + return asm volatile ("syscall" + : [ret] "={rax}" (-> usize), + : [number] "{rax}" (number), + [arg1] "{rdi}" (arg1), + : .{ .rcx = true, .r11 = true }); +} +``` -### 扩展布尔向量运算符支持 +自动升级可使用 `zig fmt`。 -布尔向量现在支持更多运算符,包括逻辑运算和比较运算。 +### 允许 @ptrCast 从单项指针转换为切片 -### 内联汇编:类型化的破坏描述符 +这是对 0.14.0 版本中 `@ptrCast` 支持切片长度转换特性的扩展。现在它还可以将单项指针转换为任意切片,返回一个引用与原始指针字节数相同的切片。 -内联汇编现在支持类型化的 `clobber` 列表,提供了更好的类型安全保证。 +```zig ptrcast-single.zig +const std = @import("std"); -### 指针转换能力增强 +test "value to byte slice with @ptrCast" { + const val: u32 = 1; + const bytes: []const u8 = @ptrCast(&val); + switch (@import("builtin").target.cpu.arch.endian()) { + .little => try std.testing.expect(std.mem.eql(u8, bytes, "\x01\x00\x00\x00")), + .big => try std.testing.expect(std.mem.eql(u8, bytes, "\x00\x00\x00\x01")), + } +} +``` -`@ptrCast` 现在允许从单项指针转换为切片,提供了更大的灵活性。 +```sh +$ zig test ptrcast-single.zig +1/1 ptrcast-single.test.value to byte slice with @ptrCast...OK +All 1 tests passed. +``` -### 调整 `undefined` 运算规则 +注意,未来计划将此能力从 `@ptrCast` 移至新的 `@memCast` 内建函数,后者在设计上更安全,有助于避免意外越界访问。详情请见 [issue #23935](https://github.com/ziglang/zig/issues/23935)。 -对 `undefined` 值的算术运算规则有所调整,任何与 `undefined` 的运算结果都将返回 `undefined`。 +### undefined 上的算术操作新规则 -### 加强有损转换检测 +Zig 0.15.x 开始规范 `undefined` 在不同场景下的行为,特别是在参与算术运算时的规则。简言之,只有那些永远不会导致非法行为的运算符才允许 `undefined` 作为操作数。其它情况,若操作数为 `undefined`,将触发非法行为(运行时报错)或编译时报错。 -对于可能导致精度损失的整数到浮点数转换,编译器会视为错误。 +通用的最佳实践是:*始终避免对 `undefined` 进行任何操作*。这样一来,这一语言变更(及未来相关变动)基本不会影响你的代码。如果你受到了此项语言变更影响,你可能会在原本可以编译的代码上见到类似的报错: -## 标准库重大变更 +```zig arith-on-undefined.zig +const a: u32 = 0; +const b: u32 = undefined; + +test "arithmetic on undefined" { + // 此处加法现在会报错 + _ = a + b; + // 解决方式就是直接避免该操作! +} +``` + +```sh +$ zig test arith-on-undefined.zig +src/download/0.15.1/release-notes/arith-on-undefined.zig:6:13: error: use of undefined value here causes illegal behavior + _ = a + b; + ^ +``` + +### 整数到浮点的损失性转换会导致编译报错 + +这类报错本就预期存在,只是直到现在才实现。若某整数值在 `comptime` 被强制转换为浮点类型,但该整数无法被该浮点数精确表示,则编译器现在会报错。例如: + +```zig lossy_int_to_float_coercion.zig +test "big float literal" { + const val: f32 = 123_456_789; + _ = val; +} +``` -### Writergate:I/O 接口完全重构 +```sh +$ zig test lossy_int_to_float_coercion.zig +src/download/0.15.1/release-notes/lossy_int_to_float_coercion.zig:2:22: error: type 'f32' cannot represent integer value '123456789' + const val: f32 = 123_456_789; + ^~~~~~~~~~~ +``` -这是本次更新中**最重大的破坏性更改**,被社区称为 "Writergate": +通常的解决办法是将整数字面量改为浮点字面量,以此显式加入浮点数的舍入规则: -- `std.io.Reader` 和 `std.io.Writer` 接口被完全重新设计 -- 新接口采用非泛型设计 -- 缓冲区位于接口之上 -- 旨在提高性能并减少不必要的拷贝 -- **需要大量代码重写** +```zig lossy_int_to_float_coercion_new.zig +test "big float literal" { + const val: f32 = 123_456_789.0; + _ = val; +} +``` -### 格式化系统调整 +```sh +$ zig test lossy_int_to_float_coercion_new.zig +1/1 lossy_int_to_float_coercion_new.test.big float literal...OK +All 1 tests passed. +``` -格式化系统进行了重要调整: +## 标准库 -- 调用自定义 `format` 方法需要使用 `{f}` 格式说明符 -- `format` 方法签名简化,移除了格式字符串和选项参数 -- 格式化打印不再处理 Unicode -- 新增了更多格式化说明符 +未归类的变更: -### `ArrayList` 非托管化 +- `fs.Dir.copyFile` 不再会因 `error.OutOfMemory` 失败 +- `fs.Dir.atomicFile` 现在要求在选项中带有 `write_buffer` +- `fs.AtomicFile` 现在有一个 `File.Writer` 字段,而不是 `File` 字段 +- `fs.File`:移除了 `WriteFileOptions`、`writeFileAll`、`writeFileAllUnseekable`,统一用 `File.Writer` 替代 +- `posix.sendfile` 移除,推荐使用 `fs.File.Reader.sendFile` -容器类型的重要变更: +### Writer 变动 -- `std.ArrayList` 现在默认采用非托管方式 -- 开发者需要显式提供分配器 -- 与其他非托管容器保持一致 -- 提供更好的内存管理控制 +[上一次相关变革(Allocgate)](https://ziglang.org/download/0.9.0/release-notes.html#Allocgate) -### 链表去泛型化 +所有现有的 `std.io` reader 和 writer 都已弃用,推荐使用全新的 `std.Io.Reader` 和 `std.Io.Writer`。这些新接口是非泛型(non-generic)的,缓冲区(buffer)在 vtable 之上。换句话说,缓冲区属于接口本身,而不是实现。这意味着 Reader 和 Writer 虽然不再是泛型类型,但在优化时依然透明:接口函数都针对 buffer 有明确的热路径,只有缓冲区满了才会触发 vtable 调用。 -链表类型进行了重构,去除了泛型化设计。 +这些更改极其破坏性,对于此我表示歉意,但我已经仔细权衡,并对这个方向充满信心。希望你能系好安全带,和 Zig 一起前行,这会物有所值。 -### HTTP 客户端和服务器 +#### 变更动机 -标准库新增了 HTTP 支持,提供内置的 HTTP 客户端和服务器功能。 +[Systems Distributed 2025 演讲:Don't Forget To Flush](https://www.youtube.com/watch?v=f30PceqQWko) + +- 老接口是泛型的,污染了包含它们的结构体,并导致所有相关函数都要用 `anytype` 泛型。新接口是具体类型。 + - 额外好处:类型具体化后,API 不容易直接作用于网络流、文件句柄或内存缓冲区,使代码更加可复用。例如更新后的 `http.Server` 不再依赖 `std.net`,而是仅操作流。 +- 老接口传递错误,但没有专用错误集合(error set),造成流的错误和 `anyerror` 类似。新接口为每个函数定义了明确的 error set,每个 error 都有可操作意义。 +- 新接口将缓冲区集成进接口本身,而不再用单独的 "BufferedReader"/"BufferedWriter" 抽象。这样对优化器更加友好,特别是在 debug 模式下。 +- 新接口支持诸如向量、填充(splatting)、直接文件传输(fd-to-fd)等高级特性,这些可以在读写器图(graph)中传递,减少系统调用、内存带宽和 CPU 使用率。 +- 新接口有 "peek" 功能,带来了用户 API 的便利与更简单易实现的实现逻辑。 + +#### 适配器 API + +如果你有旧流接口,需要转到新接口,可以这样写: + +```zig +fn foo(old_writer: anytype) !void { + var adapter = old_writer.adaptToNewApi(&.{}); + const w: *std.Io.Writer = &adapter.new_interface; + try w.print("{s}", .{"example"}); + // ... +} +``` + +#### 新的 `std.Io.Writer` 和 `std.Io.Reader` API + +这些环形缓冲区(ring buffer)有一组更方便、性能更好且非泛型的新 API。例如这样读取分隔符: + +```zig +while (reader.takeDelimiterExclusive('\n')) |line| { + // 处理每行... +} else |err| switch (err) { + error.EndOfStream, // 流未以换行结尾 + error.StreamTooLong, // 行过长,缓冲不下 + error.ReadFailed, // 可从实现中进一步诊断 + => |e| return e, +} +``` + +和其他语言的流实现相比,Zig 的流还新增了如下独特概念: + +- 读取时丢弃(discarding):可高效跳过数据。例如解压流在被请求丢弃大量数据时,可以直接跳过整个 frame。 +- 写入时填充(splatting):等同逻辑上的 `memset`,能在管道中无数据拷贝直接传递,将 O(M*N) 操作变成 O(M),有时(比如写入零)还能优化到直接 seek 跳过。 +- 写入时传送整个文件:支持 I/O 管道在操作系统支持时直接 fd-to-fd 拷贝文件。 +- 用户负责提供缓冲区,流实现决定最小缓冲区大小,等于把部分状态从实现转移到用户所提供的 buffer 上。 + +#### `std.fs.File.Reader` 和 `std.fs.File.Writer` + +`std.fs.File.Reader` 会缓存(memoize)文件句柄的关键信息: + +- `stat` 得到的大小,或相关错误 +- 当前 seek 位置 +- 尝试 seek 时发生的错误 +- 是否应定位(positional)读取 +- 是否通过 fd-to-fd(如 `sendfile`)系统调用读取,亦或普通方式 + +该类型实现了 `std.Io.Reader` 接口。 + +实际用起来非常方便。有一个具体类型缓存文件大小等信息十分好用。大部分原本在文件句柄上操作 seek 的代码应迁移到这个 API,由于定位读取的优化,往往会变成无操作(no-op),同时仍支持回退到流式读。 + +`std.fs.File.Writer` 意义类似,用于写操作。 + +#### 升级 `std.io.getStdOut().writer().print()` + +务必使用缓冲!而且别忘记 flush! + +```zig +var stdout_buffer: [1024]u8 = undefined; +var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); +const stdout = &stdout_writer.interface; + +// ... + +try stdout.print("...", .{}); + +// ... + +try stdout.flush(); +``` + +#### `std.compress.flate` 重构 + +`std.compress` 的 API 全面重组了 flate,包括 zlib 和 gzip。现在主 API 是 `std.compress.flate.Decompress`,需要传 container 参数。 + +新 API 示例: + +```zig +var decompress_buffer: [std.compress.flate.max_window_len]u8 = undefined; +var decompress: std.compress.flate.Decompress = .init(reader, .zlib, &decompress_buffer); +const decompress_reader: *std.Io.Reader = &decompress.reader; +``` + +如果 `decompress_reader` 会被整个写入某个 `*Writer`,可以用空缓冲: + +```zig +var decompress: std.compress.flate.Decompress = .init(reader, .zlib, &.{}); +const n = try decompress.streamRemaining(writer); +``` + +压缩功能已被移除。如需相关逻辑,请直接复制老代码到你的应用,或用第三方包。 + +未来会把 deflate 加回 Zig 标准库。但目前语言进展优先于标准库,本次变更也是为最终锁定[以 I/O 为接口的设计](https://ziglang.org/download/0.15.1/release-notes.html#IO-as-an-Interface)做准备。 + +特别说明: + +- 新实现不再计算校验和(check sum),可由外部实现。 +- 新实现用普通 for 循环取代了之前复杂的匹配(match)逻辑。未来希望能有专用内存拷贝工具,zstd 也可用。即便如此,实际 untar 过程快 10%,编译器自身代码减小 2%(见 #24614)。 + +#### CountingWriter 已删除 + +- 需要丢弃字节时,请用 `std.Io.Writer.Discarding`,它支持计数。 +- 需要分配字节时,请用 `std.Io.Writer.Allocating`,它能查询已分配字节数。 +- 写入固定缓冲区时,用 `std.Io.Writer.fixed`,然后查询当前指针位置。 +- 或者,不要仅为计数字节而构建 stream graph 新节点,这会破坏高效缓冲。 + +#### BufferedWriter 已删除 + +```zig +const stdout_file = std.fs.File.stdout().writer(); +var bw = std.io.bufferedWriter(stdout_file); +const stdout = bw.writer(); + +try stdout.print("Run `zig build test` to run the tests.\n", .{}); + +try bw.flush(); // 别忘记 flush! +``` + +⬇️ + +```zig +var stdout_buffer: [4096]u8 = undefined; +var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); +const stdout = &stdout_writer.interface; + +try stdout.print("Run `zig build test` to run the tests.\n", .{}); + +try stdout.flush(); // 别忘记 flush! +``` + +建议将 `stdout` 缓冲区写成全局变量。 + +#### "{f}" 格式要求:必须显式调用 format 方法 + +开启 `-freference-trace` 可以帮助你排查所有 format 字符串问题。 + +示例: + +```zig +std.debug.print("{}", .{std.zig.fmtId("example")}); +``` + +现在会报如下编译错误: + +```sh +error: ambiguous format string; specify {f} to call format method, or {any} to skip it +``` + +修正为: + +```zig +std.debug.print("{f}", .{std.zig.fmtId("example")}); +``` + +动机:消除两个易踩坑点: + +- 向结构体添加 format 方法时,如果某处用了 `{}` 会导致行为悄然变化并引发 bug。 +- 移除 format 方法时,某处用 `{}` 也会在无提示下改变输出。 + +现在,如果给结构体加了 format 方法,所有用 `{}` 的地方会直接编译报错,强制你改用 `{f}` 或明确跳过。未来,这种更改不会影响已有代码输出。 + +同理,移除 format 方法后,所有用 `{}` 的地方行为不会再悄然变化。 + +总之,用 `{f}` 总是会尝试调用 format 方法,如果不存在,会报错。 + +#### format 方法不再接受格式字符串或选项参数 + +之前: + +```zig +pub fn format( + this: @This(), + comptime format_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { ... } +``` + +⬇️ + +现在: + +```zig +pub fn format(this: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } +``` + +被删掉的 `FormatOptions` 现只用于数字类型。 + +原来依赖格式字符串的代码可选三种改法: + +- 定义不同的格式化方法 + +```zig +pub fn formatB(foo: Foo, writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } +``` + +可用 `{f}`,配合 `. {std.fmt.alt(Foo, .formatB)}` 调用。 + +- 用 `std.fmt.Alt` + +```zig +pub fn bar(foo: Foo, context: i32) std.fmt.Alt(F, F.baz) { + return .{ .data = .{ .context = context } }; +} +const F = struct { + context: i32, + pub fn baz(f: F, writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } +}; +``` + +可用 `{f}`,`.{foo.bar(1234)}` 调用。 + +- 返回实现 format 方法的 struct,再配合 `{f}` 使用 + +```zig +pub fn bar(foo: Foo, context: i32) F { + return .{ .context = 1234 }; +} +const F = struct { + context: i32, + pub fn format(f: F, writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } +}; +``` + +同样支持 `{f}`,`.{foo.bar(1234)}` 调用。 + +#### 格式化打印不再支持 Unicode + +如果你的对齐依赖 Unicode 码点,现在仅支持 ASCII/字节。此前的实现未完全支持 Unicode。如需对齐 Unicode 字符串须用真正的 Unicode 库,标准库暂不支持。 + +#### 新格式化打印符号 + +- `{t}` 简写等同于 `@tagName()` 和 `@errorName()` +- `{d}` 及其它整数格式符可用于自定义类型,会调用 `formatNumber` 方法 +- `{b64}`:以标准 base64 输出字符串 + +### 链表类型去泛型 + +现在没必要自己手写 next/prev 指针,二进制体积也略小。 + +迁移方式: + +原来: + +```zig +std.DoublyLinkedList(T).Node +``` + +变为: + +```zig +struct { + node: std.DoublyLinkedList.Node, + data: T, +} +``` + +借助 `@fieldParentPtr` 可通过 node 找到 data。 + +许多场景更推荐把 node 直接嵌入数据结构(intrusive),如果还没这么做,很可能链表不是最佳选择。 + +#### std.Progress 支持进度条转义码 + +[终端支持进度状态的转义码](https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC)。 + +构建系统(Build System)的 `--watch` 能感知失败或成功并反馈终端,等待用户输入时也会清除进度条。 + +std.Progress 新增 `setStatus` 方法及如下枚举: + +```zig +pub const Status = enum { + /// 程序正在执行任务 + working, + /// 程序操作完成,等待用户输入(不直接 exit(0)) + success, + /// 程序发生错误,等待用户输入(不直接 exit(1)) + failure, + /// 程序有错误但还在工作 + failure_working, +}; +``` + +#### HTTP 客户端与服务端 + +相关 API 和实现已完全重写。 + +服务端 API 不再依赖 std.net,只依赖 std.Io.Reader / std.Io.Writer,且可以发无限数量的 header。 + +原本: + +```zig +var read_buffer: [8000]u8 = undefined; +var server = std.http.Server.init(connection, &read_buffer); +``` + +现在: + +```zig +var recv_buffer: [4000]u8 = undefined; +var send_buffer: [4000]u8 = undefined; +var conn_reader = connection.stream.reader(&recv_buffer); +var conn_writer = connection.stream.writer(&send_buffer); +var server = std.http.Server.init(conn_reader.interface(), &conn_writer.interface); +``` + +服务端和客户端都共享 `std.http.Reader` 与 `std.http.BodyWriter`,同样只依赖 I/O 流,不依赖网络。 + +客户端升级例子: + +原本: + +```zig +var server_header_buffer: [1024]u8 = undefined; +var req = try client.open(.GET, uri, .{ + .server_header_buffer = &server_header_buffer, +}); +defer req.deinit(); + +try req.send(); +try req.wait(); + +const body_reader = try req.reader(); +// read from body_reader... + +var it = req.response.iterateHeaders(); +while (it.next()) |header| { + _ = header.name; + _ = header.value; +} +``` + +现在: + +```zig +var req = try client.request(.GET, uri, .{}); +defer req.deinit(); + +try req.sendBodiless(); +var response = try req.receiveHead(&.{}); + +// 调用 reader() 后,response.head 内字符串无效 +var it = response.head.iterateHeaders(); +while (it.next()) |header| { + _ = header.name; + _ = header.value; +} + +// 具体缓冲区大小应根据 reader 用法选择 +var reader_buffer: [100]u8 = undefined; +const body_reader = response.reader(&reader_buffer); +``` ### TLS 客户端 -新增内置 TLS 支持,可以建立安全的 TLS 连接。 +std.crypto.tls.Client 不再依赖 std.net 或 std.fs,仅依赖 std.Io.Reader / std.Io.Writer。 + +### ArrayList 默认无管理(unmanaged) + +- std.ArrayList -> std.array_list.Managed +- std.ArrayListAligned -> std.array_list.AlignedManaged + +注意:这两个类型最终都会被移除。 + +有 allocator 字段的 ArrayList 比没有更复杂,没必要。拥有 allocator 的 ArrayList 主要优势: + +- 避免用错 allocator +- 某些地方方便传递 allocator + +但缺点是: + +- 方法签名更复杂 +- 无法静态初始化 +- 更多内存占用(尤其嵌套容器时) + +最终:这些优点不值得。正确的 allocator 总是易得,也易于检查,只有一种实现更简单。实践中老用户对此很欢迎。 + +### 环形缓冲区(ring buffer) + +[标准库环形缓冲区实现太多了!](https://github.com/ziglang/zig/issues/19231) + +- `std.fifo.LinearFifo` 被移除——设计不佳,过度泛型,各种管理策略本质完全不同。 +- 绝大多数实际用途已被新 `std.Io.Writer` / `std.Io.Reader` API 覆盖。 +- `std.RingBuffer` 仅被 zstd 用,也已更换为新 API,被移除。 +- `std.compress.flate.CircularBuffer`(flate 内部用)已删除。 +- [HTTP Client 与 Server](https://ziglang.org/download/0.15.1/release-notes.html#HTTP-Client-and-Server) 也各有一个,也被删除并统一用新 API。 + +即便如此还会冒出环形缓冲区实现,比如: + +- lib/std/compress/lzma/decode/lzbuffer.zig(lzma 专用) +- lib/std/crypto/tls.zig —— 新 API 已替代 +- lib/std/debug/FixedBufferReader.zig —— 新 API 已替代 +- [某个随机 PR](https://github.com/ziglang/zig/issues/19231) + +玩笑归玩笑,未来标准库可能有通用可靠的 ring buffer,但请先问问你是否只需要 `std.Io.Reader` 或 `std.Io.Writer`。 + +### BoundedArray 被移除 + +这种结构因易于拷贝而流行,但带来隐藏成本。 + +升级建议,根据容量上限来源分类: + +- 如果是任意值,仅作为能力上限建议,不要猜。让调用方传入缓冲区 slice 或用动态分配。(例:#24699 里的 markdown) +- 如果只是栈上有限容量的类型安全,直接用 `ArrayListUnmanaged`。很方便!(例:#24699 的 test_switch_dispatch_loop.zig) + +`std.ArrayList` 现在所有 "AssumeCapacity" 的方法都有 "Bounded" 变体: + +原本: + +```zig +var stack = try std.BoundedArray(i32, 8).fromSlice(initial_stack); +``` + +现在: + +```zig +var buffer: [8]i32 = undefined; +var stack = std.ArrayListUnmanaged(i32).initBuffer(&buffer); +try stack.appendSliceBounded(initial_stack); +``` + +- 如果是有固定最大容量的有序集合,这很少见,直接自己编码管理就好。(例:#24699 Zcu.zig) +- 如果是用作可复制的可增长数组?那会无谓地到处复制未定义内存和增加泛型膨胀,建议不用。 + +### 删除和弃用内容 + +- std.fs.File.reader -> std.fs.File.deprecatedReader +- std.fs.File.writer -> std.fs.File.deprecatedWriter +- std.fmt.fmtSliceEscapeLower -> std.ascii.hexEscape +- std.fmt.fmtSliceEscapeUpper -> std.ascii.hexEscape +- std.zig.fmtEscapes -> std.zig.fmtString +- std.fmt.fmtSliceHexLower -> {x} +- std.fmt.fmtSliceHexUpper -> {X} +- std.fmt.fmtIntSizeDec -> {B} +- std.fmt.fmtIntSizeBin -> {Bi} +- std.fmt.fmtDuration -> {D} +- std.fmt.fmtDurationSigned -> {D} +- std.fmt.Formatter -> std.fmt.Alt + - 现在需显式指定 context 类型 + - 无 fmt 字符串参数 +- std.fmt.format -> std.Io.Writer.print +- std.io.GenericReader -> std.Io.Reader +- std.io.GenericWriter -> std.Io.Writer +- std.io.AnyReader -> std.Io.Reader +- std.io.AnyWriter -> std.Io.Writer +- 删除 std.io.SeekableStream + - 请用 *std.fs.File.Reader、*std.fs.File.Writer 或具体的 std.ArrayListUnmanaged,根据场景选取实现方式 +- 删除 std.io.BitReader + - 不建议此层面抽象位读取,会妨碍热循环的性能优化,请与流实现紧密耦合 +- 删除 std.io.BitWriter + - 同上 +- 删除 std.Io.LimitedReader +- 删除 std.Io.BufferedReader +- 删除 std.fifo + +## 构建系统 + +未归类的变更: + +- zig build: 在构建总结前输出一个换行 + +### 移除旧的 root_module 字段 + +Zig 0.14.0 新增了 `root_module` 字段到 `std.Build.ExecutableOptions` 及相关 API,同时废弃了此前用来指定主模块的字段 `root_source_file` 等。在 Zig 0.15.x 中,这些旧字段已被彻底移除。如果你没有在上一版本周期内迁移代码,现在会遇到类似如下的编译错误: + +```zig deprecated-addExecutable.zig +pub fn build(b: *std.Build) void { + const exe = b.addExecutable(.{ + .name = "foo", + .root_source_file = b.path("src/main.zig"), + .target = b.graph.host, + .optimize = .Debug, + }); + b.installArtifact(exe); +} + +test { + _ = &build; +} +const std = @import("std"); +``` + +```sh Shell +$ zig test deprecated-addExecutable.zig +src/download/0.15.1/release-notes/deprecated-addExecutable.zig:4:10: error: no field named 'root_source_file' in struct 'Build.ExecutableOptions' + .root_source_file = b.path("src/main.zig"), + ^~~~~~~~~~~~~~~~ +/home/ci/deps/zig-x86_64-linux-0.15.1/lib/std/Build.zig:771:31: note: struct declared here +pub const ExecutableOptions = struct { + ^~~~~~ +referenced by: + test_0: src/download/0.15.1/release-notes/deprecated-addExecutable.zig:12:10 +``` + +关于如何迁移旧代码,请参考 [Zig 0.14.0 发布说明对应章节](https://ziglang.org/download/0.14.0/release-notes.html#Creating-Artifacts-from-Existing-Modules)。 + +### macOS 文件系统监听 + +现在,`zig build` 的 `--watch` 参数已支持 macOS 系统。在 Zig 0.14.0 时,这个参数虽然可用,但对大多数编辑器表现异常;而在 Zig 0.15.x 里,这一功能已被[重新实现](https://github.com/ziglang/zig/pull/24649),采用了 macOS 的 File System Events API,确保文件系统变更监听快速且可靠。 + +所以,如果此前因 macOS 问题没有使用 `--watch`,现在可以放心使用了。尤其是你想试用 [增量编译](https://ziglang.org/download/0.15.1/release-notes.html#Incremental-Compilation) 时,推荐为 `zig build` 传递 `--watch -fincremental`。 + +### Web 界面与时间报告 + +Zig 0.14.0 提供了用于内置模糊测试 fuzzer 的实验性 Web 界面。在 0.15.x 中,这一界面被扩展为更通用的构建系统 Web 界面。可通过 `zig build --webui` 选项启用。启用后,`zig build` 进程会在构建完成后持续运行。 + +Web 界面本身主要显示所有构建步骤及其状态,同时有按钮可手动触发重新构建(所以可以作为 `zig build --watch` 流程的替代方式)。如果使用 `--fuzz`,则会暴露 [Fuzzer](https://ziglang.org/download/0.15.1/release-notes.html#Fuzzer) 相关界面,其内容与 0.14.0 基本一致。 -### 环形缓冲区 +此外,Web 界面新增了 “时间报告” 功能。只需为 `zig build` 传递 `--time-report`,即可在 Web 界面上展开查看构建图中各步骤的耗时信息。尤其是每个 `std.Build.Step.Compile`,都会有详细的子阶段统计:Zig 编译器各部分的快慢情况,以及哪些文件/声明在语义分析、生成机器码、链接阶段消耗的时间最多。 -新增了环形缓冲区类型,提供高效的循环缓冲区实现。 +![](https://ziglang.org/download/0.15.1/release-notes/build-webui.png) -### 移除 BoundedArray +这是一项较高级别的功能,非常适合定位导致编译变慢的代码片段——只需展开 "Declarations" 表格,查看最耗时的前几项。 -`BoundedArray` 被移除,建议使用 `ArrayList` 或固定大小数组替代。 +![](https://ziglang.org/download/0.15.1/release-notes/build-webui-time-report.png) -## 构建系统改进 +如果本次编译用到了 LLVM 后端,还会额外提供 LLVM pass 分阶段的耗时信息。 -### 移除隐式根模块 +## Compiler -构建系统移除了对隐式根模块的支持,现在需要在构建脚本中明确指定模块;这提高了构建配置的清晰度和准确性。 +### x86 后端 -### macOS 文件系统监视 +**Zig 0.15.x 版本在 Debug 模式下默认启用了 Zig 自实现(self-hosted)的 x86_64 代码生成后端。** -在 macOS 上新增了文件系统监视功能: +更具体来说,现在只要目标架构是 x86_64 并且使用 Debug 模式,默认都会启用该后端(除了 NetBSD、OpenBSD 和 Windows,这几个平台目前因为 [链接器](https://ziglang.org/download/0.15.1/release-notes.html#Linker) 存在缺陷,仍然默认使用 LLVM 后端)。 -- 能够在文件更改时自动触发重建 -- 支持 `--watch` 和 `--debounce` 选项 -- 显著提高开发效率 +启用自实现 x86_64 后端后,你将可以直接感受到 Zig 项目过去几年投入的成果:编译速度显著提升——大多数场景下比 LLVM 快了大约 5 倍。**而这还只是开始**;自实现 x86_64 后端是专为 [增量编译](https://ziglang.org/download/0.15.1/release-notes.html#Incremental-Compilation) 而设计的,这项功能足够稳定时,预计还会有极大加速。极致的编译速度一直是 Zig 项目的核心目标之一,我们已经默默推进多年,这次发布是阶段性成果的集中体现。 -### Web 界面和时间报告 +使用自实现 x86 后端,还能避免受上游 LLVM Bug 的影响(目前我们[正在跟踪 60 多个相关 Bug](https://github.com/ziglang/zig/issues?q=is%3Aissue%20state%3Aopen%20label%3Abackend-llvm%20label%3Aupstream))。事实上,自实现 x86 后端在我们的 “行为测试集” 上,已能通过比 LLVM 后端更多的用例(1984/2008,相比 LLVM 的 1977/2008)。换句话说,该后端对 Zig 语言的实现更为完整和准确。 -构建系统现在支持 Web 界面和详细的时间报告功能。 +当然,目前自实现 x86 后端本身也还[存在部分缺陷和 Bug](https://github.com/ziglang/zig/issues?q=is%3Aissue%20state%3Aopen%20label%3Abackend-self-hosted%20label%3Aarch-x86_64)。如果你遇到了相关问题,可以通过命令行参数 `-fllvm`,或在创建 `std.Build.Step.Compile` 时设置 `.use_llvm = true`,将 Debug 编译切换回 LLVM 后端。此外,当前自实现 x86 后端生成的机器码在性能上[略慢于 LLVM 后端](https://github.com/ziglang/zig/issues/24144)。 -### 模糊测试器 +尽管如此,在绝大多数开发场景中,自研后端已经是更优秀的选择。比如 Zig 核心开发团队已经在很长一段时间内,主要用自研 x86 后端编译 Zig 编译器,极大提升了开发效率,现在 Zig 编译器只需几秒即可构建完成,而以往用 LLVM 则要 1-2 分钟。你也可以期待在自己项目的开发体验中获得类似提升。 -Zig 0.15.1 集成了一个内置的模糊测试器: +### aarch64 后端 -- 支持进程内模糊测试 -- 提供 Web UI 界面 -- 支持实时代码覆盖率显示 -- 可以快速发现代码中的潜在问题 +在自实现 [x86 后端](https://ziglang.org/download/0.15.1/release-notes.html#x86-Backend) 已经足够成熟并启用为默认后,Jacob 将目标转向了新的架构:aarch64。该架构近年来越来越受欢迎,尤其是现代苹果电脑都基于它。因此,aarch64 成为了 Zig 项目在摆脱 LLVM 依赖的自研代码生成后端中的下一个重点。 -## 工具链更新 +这个后端目前还在开发早期阶段,Jacob 已经能基于 x86 后端的经验,尝试全新的设计思路。虽然现在下结论还为时过早,但我们预计新设计将进一步提升编译器性能(甚至有望超越自实现 x86_64 后端),并提升输出机器码的质量,最终目标是在 Debug 模式下与 LLVM 的代码生成质量一较高下。你可以在这篇 [开发日志](https://ziglang.org/devlog/2025/#2025-07-23) 里看到更多细节。 -### LLVM 20 +目前该后端已通过了 1656/1972(84%)项与 LLVM 行为一致性的测试,因此还未准备好作为默认后端启用,在实际项目中也暂时无法使用。但它正在快速进步,预计将在未来版本成为 Debug 模式下的默认选择。 -升级到 LLVM 20,带来了: +我们自实现代码生成后端的工作,是 Zig 长期计划的一部分,未来将 [使 LLVM 成为可选依赖](https://kristoff.it/blog/zig-new-relationship-llvm/),并[从编译器实现中解耦](https://github.com/ziglang/zig/issues/16270)。实现这一目标将显著提升编译速度,为 Debug 构建带来优秀的增量编译支持,甚至有可能探索 LLVM 无法高效支持的新语言特性。 -- 更好的优化支持 -- 新的目标架构支持 -- 改进的调试信息生成 -- 更好的 C++ 交互性 +### 增量编译(Incremental Compilation) -### FreeBSD 和 NetBSD libc 支持 +Zig 0.15.x 在正在开发中的增量编译(Incremental Compilation)功能上又取得了进展。该功能允许编译器只重新编译修改过的代码,从而极大提升二次编译的速度。包括文件导入变更相关的各种 Bug 在此版本中均有所修复。 -在交叉编译时新增了对动态链接的 FreeBSD 和 NetBSD libc 的支持。 +请注意,这依然属于实验性功能——目前仍有已知 Bug,可能导致错误编译或错误的编译报错。但现在已经足够稳定,可以与 `-fno-emit-bin` 结合起来可靠使用。**如果你有一个编译时间很长的庞大项目,非常建议结合使用 `--watch`、`-fincremental` 和 `-Dno-bin` 来改善编译反馈体验。**如果你不清楚如何在构建脚本里暴露 `-Dno-bin`,可以寻求社区帮助。 -### glibc 2.42 +接下来的发布周期还会继续努力,目标是将增量编译默认开启。如果你有兴趣尝鲜,可以参考 [#21165](https://github.com/ziglang/zig/issues/21165) 了解详情。 -升级到 glibc 2.42,并支持静态链接本机 glibc。 +### 多线程代码生成(Threaded Codegen) -### MinGW-w64 更新 +Zig 编译器自设计之初就考虑到了并行化。通过让编译的不同阶段在多个线程间并行运行,显著提升了编译性能。Zig 早期还是主要单线程的,但从 0.14.0 版本起,部分底层代码生成后端已经能够与前端(语义分析阶段)并发执行。Zig 0.15.x 在此基础上更进一步,实现了语义分析、代码生成、链接这几个阶段可完全并行,且代码生成本身还能进一步拆分到多个线程中去。 -MinGW-w64 工具链得到了更新,进一步改进了对 Windows 的交叉编译支持。 +与 0.14.0 相比,开启自实现后端(如 x86 后端)后,这一改进通常会带来一次明显的编译性能提升。具体提升幅度与你编译的代码结构有关,有时变化不大,有时提升可高达 50%。举个实际例子:在某台机器上,使用自实现 x86_64 后端编译 Zig 编译器,耗时从 13.8 秒降到了 10.0 秒,提升了 27%。 -### zig libc 和 zig cc 增强 +[这份开发日志](https://ziglang.org/devlog/2025/#2025-06-14) 有更深入的技术细节。总之,得益于各阶段并行执行,当你使用自研后端时,编译速度会得到显著提升。[而且现在终端会显示更详细的进度信息。](https://asciinema.org/a/bgDEbDt4AkZWORDX1YBMuKBD3) -`zig libc` 和 `zig cc` 命令的功能得到了增强: +### 支持在模块级别配置 UBSan 模式 -- 更好的 GCC 兼容性 -- 支持更多编译选项 -- 更清晰的错误报告 +现在 Zig CLI 和构建系统允许更灵活地控制 C 兼容未定义行为检测(UBSan)模式。`zig build-exe` 及相关命令现支持 `-fsanitize-c=trap` 和 `-fsanitize-c=full`,其中旧的 `-fsanitize-c` 就等价于 `-fsanitize-c=full`。 -## 版本跳跃说明 +- 选择 `full` 时,UBSan 运行时会被编译并链接进你的程序,遇到未定义行为时提供更详尽的错误信息,但相应代码体积会略大。 +- 选择 `trap` 时,会插入陷阱指令,触发未定义行为时进程会收到 `SIGILL`,但是代码体积更小。 +如未显式指定,默认模式由构建模式决定。 -Zig 官方团队选择直接从 `0.14.0` 跳跃到 `0.15.1`,这一决策反映了: +对于 [zig cc](https://ziglang.org/download/0.15.1/release-notes.html#zig-cc),在已有的 `-fsanitize=undefined` 外,现在也能理解 `-fsanitize-trap=undefined`,与 `zig build-exe` 上的 `-fsanitize-c=trap` 基本等价。 -- 本次更新包含的重大变更数量之多 -- 对编译器性能的重要改进 -- 标准库接口的重大重构 -- 为未来版本奠定更坚实的基础 +因为本次变更,`std.Build` API 里的 `sanitize_c` 字段类型从 `?bool` 替换成了 `?std.zig.SanitizeC`。如果你过去设置为 `true/false`,现在应分别切换为 `.full` 或 `.off`,以保持原有行为。 -## 升级挑战与机遇 +### 测试编译为对象文件(Compile Tests to Object File) -### 主要挑战 +通常,Zig 的测试功能会直接构建一个可执行文件。但有些场景下,你可能需要只生成测试用的对象文件而非最终可执行文件,比如让外部代码以共享库方式加载你的应用。Zig 0.15.x 针对这些需求,允许测试生成对象文件,便于后续以你希望的方式进行链接。 -1. **Writergate**:I/O 接口的完全重构需要重写大量相关代码 -2. **移除 `usingnamespace`**:需要调整导入方式 -3. **异步代码调整**:需要移除 async/await 使用 -4. **ArrayList 变更**:需要调整容器使用方式 +命令行下,可通过运行 `zig test-obj`(而不是 `zig test`)实现。 -### 主要机遇 +使用构建系统时,可通过新版 `std.Build` API,在调用 `std.Build.addTest` 时传递 `emit_object` 选项,这样返回的 `Step.Compile` 会生成对象文件。这个对象文件和其他对象一样,可以被安装用于外部使用,或直接链接到其他 build 步骤。不过注意:启用此功能后,build runner 与 test runner 不会直接通信,退回到默认的 `zig test` 方式(即用 stderr 报告测试失败)。所以如果你用到这个特性,可能还需自定义 test runner,让它能与外部测试框架协作。 -1. **编译速度提升**:5 倍的调试编译速度改进 -2. **更清晰的代码**:移除隐式行为,提高代码可读性 -3. **更好的性能**:新的 I/O 接口设计更高效 -4. **开发效率**:文件系统监视功能和模糊测试器 +### Zig Init -## 社区影响 +`zig init` 命令在本版本中配备了新版项目模板。 -这次更新对 Zig 社区产生了重要影响: +旧模板包含用于生成 Zig 模块静态库的代码,这容易让初学者误以为“生成静态库”是 Zig 代码复用的首选方式。 -- **学习曲线**:新用户需要适应新的接口设计 -- **迁移工作**:现有项目需要进行大量调整 -- **性能收益**:编译速度的显著提升改善开发体验 -- **生态系统**:库作者需要更新以适配新接口 +新的项目模板则同时提供了 Zig 模块与可执行文件的样板代码。这符合大多数开发需求,同时也展示了如何将可复用逻辑拆分到模块中、并在应用中进行调用。如果你只需生成单一类型的产物,可以直接删除不需要的部分。而保留这些内容也能温和地提醒你: -## 技术债务清理 +- 为你的库设计配套工具 +- 在你的可执行文件中方便地访问可复用逻辑 -本次更新体现了 Zig 团队对技术债务的积极清理: +现在你可以通过在 `zig init` 命令后加上 `--minimal` 或 `-m` 参数,生成极简模板。执行该命令会创建 `build.zig.zon` 文件,并在不存在时,创建仅包含 `build` 函数框架的 `build.zig` 文件。这个选项适用于已经熟悉 Zig 构建系统且主要希望方便生成带有正确指纹的 Zon 文件的用户。 -- 移除了有争议的 `usingnamespace` 特性 -- 重构了性能不佳的 I/O 接口 -- 统一了容器类型的设计理念 -- 为未来的语言演进奠定坚实基础 +## 链接器(Linker) -## 未来展望 +在本次发布周期中,Zig 的链接器仅进行了部分 Bug 修复与维护。但请注意,链接器将在[下一个发布周期](https://ziglang.org/download/0.15.1/release-notes.html#Roadmap)成为重点优化对象,目标是进一步改进增量编译(Incremental Compilation)相关体验。 -`0.15.1` 为 Zig 的未来发展奠定了重要基础: +## Fuzzer(模糊测试器) -- **编译性能**:为大型项目开发提供更好支持 -- **接口设计**:更一致、更高效的标准库 -- **开发体验**:更快的编译和更好的工具支持 -- **语言稳定性**:移除不稳定特性,为 1.0 做准备 +尽管核心团队一直对模糊测试保持极大热情,但在本发布周期内,团队成员未能投入足够精力推动 fuzzer 的进一步发展。我们在此感谢贡献者 Kendall Condon,他提交了一个 pull request [大幅提升 fuzzer 能力](https://github.com/ziglang/zig/pull/23416),目前正在耐心等待核心团队的后续协作。 -## 路线图 +## Bug 修复 -### I/O 作为接口 +[点此查看本次发布周期内关闭的 201 个 bug 报告的完整列表。](https://github.com/ziglang/zig/issues?q=is%3Aclosed+is%3Aissue+label%3Abug+milestone%3A0.15.0) -I/O 系统将继续作为接口进行发展和完善,为未来的异步编程模型做准备。 +在本次发布周期内,新的 bug 持续被发现和修复。为了简洁起见,绝大多数 bug 修复未在本发行说明中详细列出。 -## 建议 +## 本版本仍然存在已知缺陷 -对于 Zig 开发者: +Zig 依然有[已知 bug](https://github.com/ziglang/zig/issues?q=is%3Aopen+is%3Aissue+label%3Abug)、[误编译问题](https://github.com/ziglang/zig/issues?q=is%3Aopen+is%3Aissue+label%3Amiscompilation)和[回归缺陷](https://github.com/ziglang/zig/issues?q=is%3Aopen+is%3Aissue+label%3Amiscompilation)。 -1. **谨慎升级**:在测试环境中充分验证 -2. **分步迁移**:逐步调整代码以适配新接口 -3. **充分利用性能优势**:积极采用有关编译速度的改进 -4. **关注社区动态**:参与社区讨论,分享迁移经验和最佳实践 +即使使用 Zig 0.15.x,在较复杂的项目中工作,也可能需要你主动参与到开发流程当中,一起反馈和解决问题。 -`0.15.1` 是 Zig 发展历程中的一个重要里程碑,虽然带来了较多的破坏性变更,但为语言的长期发展和性能提升奠定了坚实的基础。这标志着语言稳定化的最后阶段,为 1.0 版本的正式发布做好了充分准备。 +当 Zig 进入 1.0.0 正式版后,Tier 1 支持将会增加专门的 bug 管理政策作为强制要求。 \ No newline at end of file From 5d8532528854489279e650a5769bcffa996e6837 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Wed, 22 Oct 2025 13:32:12 +0800 Subject: [PATCH 02/11] =?UTF-8?q?docs(course/update):=20=E8=A7=84=E8=8C=83?= =?UTF-8?q?=20Zig=200.15.1=20=E5=8F=91=E5=B8=83=E8=AF=B4=E6=98=8E=E7=9A=84?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整系统版本要求表格的对齐和间距 - 修正中文排版中不规范的空格和标点符号 - 统一嵌套列表项的缩进格式(4空格改为2空格) - 修复 Markdown 语法转义和标记符号 - 移除文档中的尾部空白和多余空行 --- course/update/0.15.1-description.md | 53 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/course/update/0.15.1-description.md b/course/update/0.15.1-description.md index 37d9db3d..437303a7 100644 --- a/course/update/0.15.1-description.md +++ b/course/update/0.15.1-description.md @@ -16,16 +16,16 @@ Zig 目前默认使用 x86 后端,调试编译速度提高了 **5** 倍;aarc ## 系统最低版本要求 -| 操作系统(Operating System) | 最低版本要求 (Minimum Version) | -| :------------------------- | :-----------------------------: | -| Dragonfly BSD | 6.0 | -| FreeBSD | 14.0 | -| Linux | 5.10 | -| NetBSD | 10.1 | -| OpenBSD | 7.6 | -| macOS | 13.0 | -| Solaris | 11 | -| Windows | 10 | +| 操作系统(Operating System) | 最低版本要求(Minimum Version) | +| :--------------------------- | :------------------------------: | +| Dragonfly BSD | 6.0 | +| FreeBSD | 14.0 | +| Linux | 5.10 | +| NetBSD | 10.1 | +| OpenBSD | 7.6 | +| macOS | 13.0 | +| Solaris | 11 | +| Windows | 10 | ## 语言变动 @@ -157,7 +157,7 @@ pub const Foo = struct { }; ``` -替代写法基于前述的命名空间思想:其实**恰当的命名空间划分是好事**。Mixins 也可以用命名空间来组织。比如把 `incrementCounter` 和 `resetCounter` 名称中的 “counter” 前缀直接变成一个嵌套字段,这样 `foo.counter.increment()` 替代 `foo.incrementCounter()`,语义更直观。 +替代写法基于前述的命名空间思想:其实**恰当的命名空间划分是好事**。Mixins 也可以用命名空间来组织。比如把 `incrementCounter` 和 `resetCounter` 名称中的“counter”前缀直接变成一个嵌套字段,这样 `foo.counter.increment()` 替代 `foo.incrementCounter()`,语义更直观。 实现方式可利用零位字段(zero-bit field)和 `@fieldParentPtr`: @@ -182,13 +182,12 @@ pub const Foo = struct { }; ``` -这样写后,调用就是 `foo.counter.increment()`,不再是 `foo.incrementCounter()`。通过零位字段成功将 mixin “命名空间化”。 +这样写后,调用就是 `foo.counter.increment()`,不再是 `foo.incrementCounter()`。通过零位字段成功将 mixin“命名空间化”。 实际上,这比以前更灵活,也能让混入体包含如字段等更多内容。比如本例中甚至可以将 `count` 字段本身都移到 `CounterMixin` 内部(虽然这种场景下其实不算 mixin,因为只剩状态本身)。 更重要的是,如果某些复杂混入需要额外状态,这种办法也能避免每个站点都重复字段声明,维护起来更方便。 - ### 移除 async 和 await 关键字 `async` 和 `await` 关键字已被移除,`@frameSize` 也已删除。 @@ -292,7 +291,7 @@ All 1 tests passed. Zig 0.15.x 开始规范 `undefined` 在不同场景下的行为,特别是在参与算术运算时的规则。简言之,只有那些永远不会导致非法行为的运算符才允许 `undefined` 作为操作数。其它情况,若操作数为 `undefined`,将触发非法行为(运行时报错)或编译时报错。 -通用的最佳实践是:*始终避免对 `undefined` 进行任何操作*。这样一来,这一语言变更(及未来相关变动)基本不会影响你的代码。如果你受到了此项语言变更影响,你可能会在原本可以编译的代码上见到类似的报错: +通用的最佳实践是:_始终避免对 `undefined` 进行任何操作_。这样一来,这一语言变更(及未来相关变动)基本不会影响你的代码。如果你受到了此项语言变更影响,你可能会在原本可以编译的代码上见到类似的报错: ```zig arith-on-undefined.zig const a: u32 = 0; @@ -368,7 +367,7 @@ All 1 tests passed. [Systems Distributed 2025 演讲:Don't Forget To Flush](https://www.youtube.com/watch?v=f30PceqQWko) - 老接口是泛型的,污染了包含它们的结构体,并导致所有相关函数都要用 `anytype` 泛型。新接口是具体类型。 - - 额外好处:类型具体化后,API 不容易直接作用于网络流、文件句柄或内存缓冲区,使代码更加可复用。例如更新后的 `http.Server` 不再依赖 `std.net`,而是仅操作流。 + - 额外好处:类型具体化后,API 不容易直接作用于网络流、文件句柄或内存缓冲区,使代码更加可复用。例如更新后的 `http.Server` 不再依赖 `std.net`,而是仅操作流。 - 老接口传递错误,但没有专用错误集合(error set),造成流的错误和 `anyerror` 类似。新接口为每个函数定义了明确的 error set,每个 error 都有可操作意义。 - 新接口将缓冲区集成进接口本身,而不再用单独的 "BufferedReader"/"BufferedWriter" 抽象。这样对优化器更加友好,特别是在 debug 模式下。 - 新接口支持诸如向量、填充(splatting)、直接文件传输(fd-to-fd)等高级特性,这些可以在读写器图(graph)中传递,减少系统调用、内存带宽和 CPU 使用率。 @@ -405,7 +404,7 @@ while (reader.takeDelimiterExclusive('\n')) |line| { 和其他语言的流实现相比,Zig 的流还新增了如下独特概念: - 读取时丢弃(discarding):可高效跳过数据。例如解压流在被请求丢弃大量数据时,可以直接跳过整个 frame。 -- 写入时填充(splatting):等同逻辑上的 `memset`,能在管道中无数据拷贝直接传递,将 O(M*N) 操作变成 O(M),有时(比如写入零)还能优化到直接 seek 跳过。 +- 写入时填充(splatting):等同逻辑上的 `memset`,能在管道中无数据拷贝直接传递,将 O(M\*N) 操作变成 O(M),有时(比如写入零)还能优化到直接 seek 跳过。 - 写入时传送整个文件:支持 I/O 管道在操作系统支持时直接 fd-to-fd 拷贝文件。 - 用户负责提供缓冲区,流实现决定最小缓冲区大小,等于把部分状态从实现转移到用户所提供的 buffer 上。 @@ -809,19 +808,19 @@ try stack.appendSliceBounded(initial_stack); - std.fmt.fmtDuration -> {D} - std.fmt.fmtDurationSigned -> {D} - std.fmt.Formatter -> std.fmt.Alt - - 现在需显式指定 context 类型 - - 无 fmt 字符串参数 + - 现在需显式指定 context 类型 + - 无 fmt 字符串参数 - std.fmt.format -> std.Io.Writer.print - std.io.GenericReader -> std.Io.Reader - std.io.GenericWriter -> std.Io.Writer - std.io.AnyReader -> std.Io.Reader - std.io.AnyWriter -> std.Io.Writer - 删除 std.io.SeekableStream - - 请用 *std.fs.File.Reader、*std.fs.File.Writer 或具体的 std.ArrayListUnmanaged,根据场景选取实现方式 + - 请用 *std.fs.File.Reader、*std.fs.File.Writer 或具体的 std.ArrayListUnmanaged,根据场景选取实现方式 - 删除 std.io.BitReader - - 不建议此层面抽象位读取,会妨碍热循环的性能优化,请与流实现紧密耦合 + - 不建议此层面抽象位读取,会妨碍热循环的性能优化,请与流实现紧密耦合 - 删除 std.io.BitWriter - - 同上 + - 同上 - 删除 std.Io.LimitedReader - 删除 std.Io.BufferedReader - 删除 std.fifo @@ -879,7 +878,7 @@ Zig 0.14.0 提供了用于内置模糊测试 fuzzer 的实验性 Web 界面。 Web 界面本身主要显示所有构建步骤及其状态,同时有按钮可手动触发重新构建(所以可以作为 `zig build --watch` 流程的替代方式)。如果使用 `--fuzz`,则会暴露 [Fuzzer](https://ziglang.org/download/0.15.1/release-notes.html#Fuzzer) 相关界面,其内容与 0.14.0 基本一致。 -此外,Web 界面新增了 “时间报告” 功能。只需为 `zig build` 传递 `--time-report`,即可在 Web 界面上展开查看构建图中各步骤的耗时信息。尤其是每个 `std.Build.Step.Compile`,都会有详细的子阶段统计:Zig 编译器各部分的快慢情况,以及哪些文件/声明在语义分析、生成机器码、链接阶段消耗的时间最多。 +此外,Web 界面新增了“时间报告”功能。只需为 `zig build` 传递 `--time-report`,即可在 Web 界面上展开查看构建图中各步骤的耗时信息。尤其是每个 `std.Build.Step.Compile`,都会有详细的子阶段统计:Zig 编译器各部分的快慢情况,以及哪些文件/声明在语义分析、生成机器码、链接阶段消耗的时间最多。 ![](https://ziglang.org/download/0.15.1/release-notes/build-webui.png) @@ -889,7 +888,7 @@ Web 界面本身主要显示所有构建步骤及其状态,同时有按钮可 如果本次编译用到了 LLVM 后端,还会额外提供 LLVM pass 分阶段的耗时信息。 -## Compiler +## Compiler ### x86 后端 @@ -899,7 +898,7 @@ Web 界面本身主要显示所有构建步骤及其状态,同时有按钮可 启用自实现 x86_64 后端后,你将可以直接感受到 Zig 项目过去几年投入的成果:编译速度显著提升——大多数场景下比 LLVM 快了大约 5 倍。**而这还只是开始**;自实现 x86_64 后端是专为 [增量编译](https://ziglang.org/download/0.15.1/release-notes.html#Incremental-Compilation) 而设计的,这项功能足够稳定时,预计还会有极大加速。极致的编译速度一直是 Zig 项目的核心目标之一,我们已经默默推进多年,这次发布是阶段性成果的集中体现。 -使用自实现 x86 后端,还能避免受上游 LLVM Bug 的影响(目前我们[正在跟踪 60 多个相关 Bug](https://github.com/ziglang/zig/issues?q=is%3Aissue%20state%3Aopen%20label%3Abackend-llvm%20label%3Aupstream))。事实上,自实现 x86 后端在我们的 “行为测试集” 上,已能通过比 LLVM 后端更多的用例(1984/2008,相比 LLVM 的 1977/2008)。换句话说,该后端对 Zig 语言的实现更为完整和准确。 +使用自实现 x86 后端,还能避免受上游 LLVM Bug 的影响(目前我们[正在跟踪 60 多个相关 Bug](https://github.com/ziglang/zig/issues?q=is%3Aissue%20state%3Aopen%20label%3Abackend-llvm%20label%3Aupstream))。事实上,自实现 x86 后端在我们的“行为测试集”上,已能通过比 LLVM 后端更多的用例(1984/2008,相比 LLVM 的 1977/2008)。换句话说,该后端对 Zig 语言的实现更为完整和准确。 当然,目前自实现 x86 后端本身也还[存在部分缺陷和 Bug](https://github.com/ziglang/zig/issues?q=is%3Aissue%20state%3Aopen%20label%3Abackend-self-hosted%20label%3Aarch-x86_64)。如果你遇到了相关问题,可以通过命令行参数 `-fllvm`,或在创建 `std.Build.Step.Compile` 时设置 `.use_llvm = true`,将 Debug 编译切换回 LLVM 后端。此外,当前自实现 x86 后端生成的机器码在性能上[略慢于 LLVM 后端](https://github.com/ziglang/zig/issues/24144)。 @@ -937,7 +936,7 @@ Zig 编译器自设计之初就考虑到了并行化。通过让编译的不同 - 选择 `full` 时,UBSan 运行时会被编译并链接进你的程序,遇到未定义行为时提供更详尽的错误信息,但相应代码体积会略大。 - 选择 `trap` 时,会插入陷阱指令,触发未定义行为时进程会收到 `SIGILL`,但是代码体积更小。 -如未显式指定,默认模式由构建模式决定。 + 如未显式指定,默认模式由构建模式决定。 对于 [zig cc](https://ziglang.org/download/0.15.1/release-notes.html#zig-cc),在已有的 `-fsanitize=undefined` 外,现在也能理解 `-fsanitize-trap=undefined`,与 `zig build-exe` 上的 `-fsanitize-c=trap` 基本等价。 @@ -951,7 +950,7 @@ Zig 编译器自设计之初就考虑到了并行化。通过让编译的不同 使用构建系统时,可通过新版 `std.Build` API,在调用 `std.Build.addTest` 时传递 `emit_object` 选项,这样返回的 `Step.Compile` 会生成对象文件。这个对象文件和其他对象一样,可以被安装用于外部使用,或直接链接到其他 build 步骤。不过注意:启用此功能后,build runner 与 test runner 不会直接通信,退回到默认的 `zig test` 方式(即用 stderr 报告测试失败)。所以如果你用到这个特性,可能还需自定义 test runner,让它能与外部测试框架协作。 -### Zig Init +### Zig Init `zig init` 命令在本版本中配备了新版项目模板。 @@ -984,4 +983,4 @@ Zig 依然有[已知 bug](https://github.com/ziglang/zig/issues?q=is%3Aopen+is%3 即使使用 Zig 0.15.x,在较复杂的项目中工作,也可能需要你主动参与到开发流程当中,一起反馈和解决问题。 -当 Zig 进入 1.0.0 正式版后,Tier 1 支持将会增加专门的 bug 管理政策作为强制要求。 \ No newline at end of file +当 Zig 进入 1.0.0 正式版后,Tier 1 支持将会增加专门的 bug 管理政策作为强制要求。 From 8055977384638a70c110a7e5d0a44677d1531fbd Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Wed, 22 Oct 2025 13:45:02 +0800 Subject: [PATCH 03/11] =?UTF-8?q?docs(course/update):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20Zig=200.15.1=20=E5=8F=91=E5=B8=83=E8=AF=B4=E6=98=8E=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E5=B7=A5=E5=85=B7=E9=93=BE=E5=92=8C=E8=B7=AF=E7=BA=BF?= =?UTF-8?q?=E5=9B=BE=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增工具链(Toolchain)章节,包括 LLVM 20、FreeBSD/NetBSD 交叉编译支持、glibc 2.42 等内容 - 新增路线图(Roadmap)章节,说明 0.16.0 版本的核心主题和重要里程碑 - 调整"本版本仍然存在已知缺陷"的标题级别从 h2 改为 h3 --- course/update/0.15.1-description.md | 63 ++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/course/update/0.15.1-description.md b/course/update/0.15.1-description.md index 437303a7..80020b07 100644 --- a/course/update/0.15.1-description.md +++ b/course/update/0.15.1-description.md @@ -977,10 +977,71 @@ Zig 编译器自设计之初就考虑到了并行化。通过让编译的不同 在本次发布周期内,新的 bug 持续被发现和修复。为了简洁起见,绝大多数 bug 修复未在本发行说明中详细列出。 -## 本版本仍然存在已知缺陷 +### 本版本仍然存在已知缺陷 Zig 依然有[已知 bug](https://github.com/ziglang/zig/issues?q=is%3Aopen+is%3Aissue+label%3Abug)、[误编译问题](https://github.com/ziglang/zig/issues?q=is%3Aopen+is%3Aissue+label%3Amiscompilation)和[回归缺陷](https://github.com/ziglang/zig/issues?q=is%3Aopen+is%3Aissue+label%3Amiscompilation)。 即使使用 Zig 0.15.x,在较复杂的项目中工作,也可能需要你主动参与到开发流程当中,一起反馈和解决问题。 当 Zig 进入 1.0.0 正式版后,Tier 1 支持将会增加专门的 bug 管理政策作为强制要求。 + +## 工具链(Toolchain) + +### LLVM 20 + +本次 Zig 升级到了 LLVM [20.1.8](https://releases.llvm.org/20.1.0/docs/ReleaseNotes.html)。此升级涵盖 Clang(`zig cc`/`zig c++`)、libc++、libc++abi、libunwind 以及 libtsan。 + +Zig 现已支持使用 LLVM 的 SPIR-V 后端(backend)。请注意,自托管的 SPIR-V 后端仍是默认选项。如需使用 LLVM 后端,可通过 `-fllvm` 参数进行构建。 + +### 交叉编译时支持 FreeBSD 动态链接 libc + +Zig 现在通过为动态链接的 libc 提供桩库(stub libraries),允许交叉编译到 FreeBSD 14+,这与 glibc 的交叉编译方式类似。此外,还会一并提供所有系统和 libc 头文件。 + +### 交叉编译时支持 NetBSD 动态链接 libc + +Zig 现在通过为动态链接的 libc 提供桩库,支持交叉编译到 NetBSD 10.1+,方式同 glibc 类似。同时也会提供所有系统及 libc 头文件。 + +### glibc 2.42 + +交叉编译时现已可用 glibc 2.42 版本。 + +#### 允许静态链接本地 glibc + +Zig 现在允许静态链接本地 glibc。尽管这通常不是一个好主意,但对于某些不依赖 glibc 动态特性的特殊场景(如 NSS、iconv 等内部依赖动态链接的功能未被使用时),可以这样做。 + +需要注意,若使用 Zig 内置的 glibc 进行交叉编译,该 glibc 仅以动态库形式提供,因此不适用静态链接。 + +### MinGW-w64 + +本版本将内置的 MinGW-w64 升级到提交号 `38c8142f660b6ba11e7c408f2de1e9f8bfaf839e`。 + +### zig libc + +本次发布开始尝试在 Zig 提供的静态链接 libc(目前包括 musl、wasi-libc 和 [MinGW-w64](https://ziglang.org/download/0.15.1/release-notes.html#MinGW-w64))之间共享代码。我们在新的 zig libc 库中用 Zig 代码重新实现了这些 libc 的通用函数。这意味着每个函数都将有唯一的权威实现,今后无需再分别修改上述项目的第三方 libc 代码即可改进实现。我们的长期目标是彻底摆脱对这些 libc 上游 C 实现代码的依赖,仅保留其头文件(headers),但这还需要很多工作。 + +这个工作非常欢迎社区贡献。如果你对此感兴趣,可查看 [issue #2879](https://github.com/ziglang/zig/issues/2879) 参与。 + +### zig cc + +zig cc 现在能正确识别 `-static` 和 `-dynamic` 标志。最值得注意的是,这允许静态链接本地 glibc,以及动态链接交叉编译的 musl。 + +### zig objcopy 功能回退 + +很抱歉,相关代码未达到质量标准,需重新设计。一部分功能仍可使用,另外一些功能会报“unimplemented”(未实现)的错误。详见 #24522。 + +## 路线图(Roadmap) + +0.16.0 版本周期的两大核心主题将是异步 I/O 和 aarch64 后端。 + +下一步要达成的重要里程碑包括: + +- 引入 I/O 作为 Interface(接口类型) +- 使 aarch64 后端成为 debug 模式下的默认后端 +- 改进链接器实现,消除对 LLD 的依赖,并支持增量编译 +- 加强内置 Fuzzer,使其具备与 AFL 及其它先进模糊测试工具竞争的能力 + +### I/O 作为接口 + +未来,Zig 将把全部文件系统、网络、定时器、同步机制,以及任何可能阻塞(block)的内容,都重构为全新的 `std.Io` 接口。所有涉及 I/O 的代码都需要接收一个 `Io` 实例,类似于所有需要内存分配的代码都需要一个 `Allocator` 实例。 + +这将让你可以编写对应用并发模型无感知的、可复用且高性能的包(package),支持[异步机制](https://kristoff.it/blog/asynchrony-is-not-concurrency/),发现更多类型的 bug,并让事件循环(event loop)成为 Zig 生态中的“一等公民”。 From b297b2c82df8cbce447902fbf8a9e77fb415c49a Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Wed, 22 Oct 2025 13:45:28 +0800 Subject: [PATCH 04/11] =?UTF-8?q?docs(course/update):=20=E8=A7=84=E8=8C=83?= =?UTF-8?q?=20Zig=200.15.1=20=E7=B3=BB=E7=BB=9F=E8=A6=81=E6=B1=82=E8=A1=A8?= =?UTF-8?q?=E6=A0=BC=E7=9A=84=E5=88=97=E5=AF=B9=E9=BD=90=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整表头分隔符的长度 - 统一系统最低版本要求列各单元格的间距 - 改善表格的 Markdown 渲染一致性 --- course/update/0.15.1-description.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/course/update/0.15.1-description.md b/course/update/0.15.1-description.md index 80020b07..e408ddff 100644 --- a/course/update/0.15.1-description.md +++ b/course/update/0.15.1-description.md @@ -17,15 +17,15 @@ Zig 目前默认使用 x86 后端,调试编译速度提高了 **5** 倍;aarc ## 系统最低版本要求 | 操作系统(Operating System) | 最低版本要求(Minimum Version) | -| :--------------------------- | :------------------------------: | -| Dragonfly BSD | 6.0 | -| FreeBSD | 14.0 | -| Linux | 5.10 | -| NetBSD | 10.1 | -| OpenBSD | 7.6 | -| macOS | 13.0 | -| Solaris | 11 | -| Windows | 10 | +| :--------------------------- | :-----------------------------: | +| Dragonfly BSD | 6.0 | +| FreeBSD | 14.0 | +| Linux | 5.10 | +| NetBSD | 10.1 | +| OpenBSD | 7.6 | +| macOS | 13.0 | +| Solaris | 11 | +| Windows | 10 | ## 语言变动 From 14053622fada7ea10a07065d60b854983e8550b0 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Wed, 22 Oct 2025 13:46:32 +0800 Subject: [PATCH 05/11] =?UTF-8?q?chore(course):=20=E5=88=A0=E9=99=A4=20Zig?= =?UTF-8?q?=200.15.0=20=E6=9E=84=E5=BB=BA=E9=94=99=E8=AF=AF=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除临时的构建错误和输出日志 - 清理不必要的调试文件 - 简化项目文件结构 --- 15_zig_build_fix.txt | 262 ------------------------------------------- 1 file changed, 262 deletions(-) delete mode 100644 15_zig_build_fix.txt diff --git a/15_zig_build_fix.txt b/15_zig_build_fix.txt deleted file mode 100644 index 5e084769..00000000 --- a/15_zig_build_fix.txt +++ /dev/null @@ -1,262 +0,0 @@ - -src\main.zig:10:31: error: root source file struct 'Io' has no member named 'getStdOut' - const stdout_file = std.io.getStdOut().writer(); - ~~~~~~^~~~~~~~~~ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Io.zig:1:1: note: struct declared here -const builtin = @import("builtin"); -^~~~~ -referenced by: - callMain [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:627:37 - WinStartup: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:443:53 - 2 reference(s) hidden; use '-freference-trace=4' to see all references -error: the following command failed with 1 compilation errors: -"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c -ode\\15\\build_system\\basic\\src\\main.zig" --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name zi -g --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- - -Build Summary: 0/3 steps succeeded; 1 failed -install transitive failure -└─ install zig transitive failure - └─ compile exe zig Debug native 1 errors - -error: the following build command failed with exit code 1: -.zig-cache\o\22df4cf7dbdbf962cf23936c61889573\build.exe C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\zig.exe C:\Users\jin\scoop\app -s\zig-dev\0.15.0-dev.1283\lib C:\Users\jin\code\zig-course\course\code\15\build_system\basic .zig-cache C:\Users\jin\AppData\Local\zig - --seed 0x625a5d8f -Z22ccef05181dde7e -install -└─ install zig - └─ compile exe zig Debug native 1 errors -src\main.zig:10:31: error: root source file struct 'Io' has no member named 'getStdOut' - const stdout_file = std.io.getStdOut().writer(); - ~~~~~~^~~~~~~~~~ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Io.zig:1:1: note: struct declared here -const builtin = @import("builtin"); -^~~~~ -referenced by: - callMain [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:627:37 - WinStartup: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:443:53 - 2 reference(s) hidden; use '-freference-trace=4' to see all references -error: the following command failed with 1 compilation errors: -"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -fno-strip -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course -\\course\\code\\15\\build_system\\cli\\src\\main.zig" --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" ---name zig --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- - -Build Summary: 0/3 steps succeeded; 1 failed -install transitive failure -└─ install zig transitive failure - └─ compile exe zig Debug native 1 errors - -error: the following build command failed with exit code 1: -.zig-cache\o\5bd10d6c5035afe76329d62536957327\build.exe C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\zig.exe C:\Users\jin\scoop\app -s\zig-dev\0.15.0-dev.1283\lib C:\Users\jin\code\zig-course\course\code\15\build_system\cli .zig-cache C:\Users\jin\AppData\Local\zig - --seed 0x59ad36e3 -Z06a0c06bd5c06800 -build.zig:11:18: error: no field or member function named 'addStaticLibrary' in 'Build' - const lib = b.addStaticLibrary(.{ - ~^~~~~~~~~~~~~~~~~ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:1:1: note: struct declared here -const std = @import("std.zig"); -^~~~~ -build.zig:11:18: note: method invocation only supports up to one level of implicit pointer dereferencing -build.zig:11:18: note: use '.*' to dereference pointer -referenced by: - runBuild__anon_32108: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:2211:33 - main: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\compiler\build_runner.zig:352:29 - 4 reference(s) hidden; use '-freference-trace=6' to see all references -build.zig:13:10: error: no field named 'root_source_file' in struct 'Build.ExecutableOptions' - .root_source_file = b.path("src/main.zig"), - ^~~~~~~~~~~~~~~~ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:768:31: note: struct declared here -pub const ExecutableOptions = struct { - ^~~~~~ -referenced by: - runBuild__anon_32108: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:2211:33 - main: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\compiler\build_runner.zig:352:29 - 4 reference(s) hidden; use '-freference-trace=6' to see all references -install -└─ install hello - └─ compile exe hello Debug native 1 errors -src\main.zig:10:31: error: root source file struct 'Io' has no member named 'getStdOut' - const stdout_file = std.io.getStdOut().writer(); - ~~~~~~^~~~~~~~~~ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Io.zig:1:1: note: struct declared here -const builtin = @import("builtin"); -^~~~~ -referenced by: - callMain [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:627:37 - WinStartup: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:443:53 - 2 reference(s) hidden; use '-freference-trace=4' to see all references -error: the following command failed with 1 compilation errors: -"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c -ode\\15\\build_system\\step\\src\\main.zig" --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name hel -lo --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- - -Build Summary: 0/3 steps succeeded; 1 failed -install transitive failure -└─ install hello transitive failure - └─ compile exe hello Debug native 1 errors - -error: the following build command failed with exit code 1: -.zig-cache\o\e269fd4c82996e8b6efaa38022c4a79b\build.exe C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\zig.exe C:\Users\jin\scoop\app -s\zig-dev\0.15.0-dev.1283\lib C:\Users\jin\code\zig-course\course\code\15\build_system\step .zig-cache C:\Users\jin\AppData\Local\zig ---seed 0x1738a8dc -Z7116767a471854aa -install -└─ install zig - └─ compile exe zig Debug native 1 errors -src\main.zig:10:31: error: root source file struct 'Io' has no member named 'getStdOut' - const stdout_file = std.io.getStdOut().writer(); - ~~~~~~^~~~~~~~~~ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Io.zig:1:1: note: struct declared here -const builtin = @import("builtin"); -^~~~~ -referenced by: - callMain [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:627:37 - WinStartup: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:443:53 - 2 reference(s) hidden; use '-freference-trace=4' to see all references -error: the following command failed with 1 compilation errors: -"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c -ode\\15\\build_system\\test\\src\\main.zig" --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name zig - --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- - -Build Summary: 0/3 steps succeeded; 1 failed -install transitive failure -└─ install zig transitive failure - └─ compile exe zig Debug native 1 errors - -error: the following build command failed with exit code 1: -.zig-cache\o\52bd7ebf7a1e3f67b283bc7a1b1460ca\build.exe C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\zig.exe C:\Users\jin\scoop\app -s\zig-dev\0.15.0-dev.1283\lib C:\Users\jin\code\zig-course\course\code\15\build_system\test .zig-cache C:\Users\jin\AppData\Local\zig ---seed 0xca605874 -Z5dd6b5163b24fc08 -build.zig:19:10: error: no field named 'target' in struct 'Build.ExecutableOptions' - .target = target, - ^~~~~~ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:768:31: note: struct declared here -pub const ExecutableOptions = struct { - ^~~~~~ -referenced by: - runBuild__anon_32108: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:2211:33 - main: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\compiler\build_runner.zig:352:29 - 4 reference(s) hidden; use '-freference-trace=6' to see all references -C:\Users\jin\AppData\Local\zig\p\zig_msgpack-0.0.7-evvueE3MAADy-2EAgCGUYIf1tHC9-z4n2sDIldvTZcY8\build.zig:21:10: error: no field named - 'root_source_file' in struct 'Build.TestOptions' - .root_source_file = b.path(b.pathJoin(&.{ "src", "test.zig" })), - ^~~~~~~~~~~~~~~~ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:853:25: note: struct declared here -pub const TestOptions = struct { - ^~~~~~ -referenced by: - runBuild__anon_81738: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:2211:33 - dependencyInner__anon_78976: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:2192:29 - 8 reference(s) hidden; use '-freference-trace=10' to see all references -C:\Users\jin\AppData\Local\zig\p\zig_msgpack-0.0.7-evvueE3MAADy-2EAgCGUYIf1tHC9-z4n2sDIldvTZcY8\build.zig:33:10: error: no field named - 'root_source_file' in struct 'Build.ObjectOptions' - .root_source_file = b.path(b.pathJoin(&.{ "src", "msgpack.zig" })), - ^~~~~~~~~~~~~~~~ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:800:27: note: struct declared here -pub const ObjectOptions = struct { - ^~~~~~ -install -└─ install assembly_fixed - └─ compile exe assembly_fixed Debug native 1 errors -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:614:46: error: root source file struct 'assembly_fixed' has no membe -r named 'main' - const ReturnType = @typeInfo(@TypeOf(root.main)).@"fn".return_type.?; - ~~~~^~~~~ -course\code\15\assembly_fixed.zig:2:1: note: struct declared here - -^ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:443:53: note: called inline here - std.os.windows.ntdll.RtlExitUserProcess(callMain()); - ~~~~~~~~^~ -referenced by: - comptime: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:68:30 - start: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\std.zig:100:27 - 1 reference(s) hidden; use '-freference-trace=3' to see all references -error: the following command failed with 1 compilation errors: -"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c -ode\\15\\assembly_fixed.zig" -lc --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name assembly_fixed - --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- -install -└─ install echo_tcp_server - └─ compile exe echo_tcp_server Debug native 12 errors -error: lld-link: undefined symbol: WSAPoll - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1728 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.poll) -error: lld-link: undefined symbol: ioctlsocket - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:3634 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.socket) - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:4028 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.setSockFlags) -error: lld-link: undefined symbol: WSAGetLastError - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:3635 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.socket) - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:6658 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.setsockopt) - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:3776 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.bind) - note: referenced 7 more times -error: lld-link: undefined symbol: setsockopt - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:6656 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.setsockopt) -error: lld-link: undefined symbol: closesocket - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1671 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.closesocket) -error: lld-link: undefined symbol: WSAStartup - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1566 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.WSAStartup) -error: lld-link: undefined symbol: WSASocketW - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1642 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.WSASocketW) -error: lld-link: undefined symbol: bind - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1663 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.bind) -error: lld-link: undefined symbol: listen - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1667 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.listen) -error: lld-link: undefined symbol: getsockname - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1686 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.getsockname) -error: lld-link: undefined symbol: accept - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1682 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.accept) -error: lld-link: undefined symbol: WSASend - note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\net.zig:2083 - note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(net.Stream.Writer__struct_31094.sendBuf -s) -error: the following command failed with 12 compilation errors: -"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c -ode\\15\\echo_tcp_server.zig" -lc --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name echo_tcp_serv -er --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- -install -└─ install hello_world - └─ compile exe hello_world Debug native 1 errors -course\code\15\hello_world.zig:22:30: error: root source file struct 'Io' has no member named 'getStdOut' - const stdout = std.io.getStdOut().writer(); - ~~~~~~^~~~~~~~~~ -C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Io.zig:1:1: note: struct declared here -const builtin = @import("builtin"); -^~~~~ -referenced by: - main: course\code\15\hello_world.zig:3:17 - callMain [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:627:37 - callMainWithArgs [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:587:20 - main: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:602:28 - 1 reference(s) hidden; use '-freference-trace=5' to see all references -error: the following command failed with 1 compilation errors: -"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c -ode\\15\\hello_world.zig" -lc --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name hello_world --zig --lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- - -Build Summary: 118/125 steps succeeded; 3 failed; 10/10 tests passed -install transitive failure -├─ install assembly_fixed transitive failure -│ └─ compile exe assembly_fixed Debug native 1 errors -├─ install echo_tcp_server transitive failure -│ └─ compile exe echo_tcp_server Debug native 12 errors -└─ install hello_world transitive failure - └─ compile exe hello_world Debug native 1 errors - -error: the following build command failed with exit code 1: -.zig-cache\o\48912e7b0e7a403214ce8e205d9457a3\build.exe C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\zig.exe C:\Users\jin\scoop\app -s\zig-dev\0.15.0-dev.1283\lib C:\Users\jin\code\zig-course .zig-cache C:\Users\jin\AppData\Local\zig --seed 0x9311a7fb -Z19e9ce87436cf -a8c - From 737ac32db20fd7835ec2ddd015f54fa21c62e8a5 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Wed, 22 Oct 2025 14:50:51 +0800 Subject: [PATCH 06/11] =?UTF-8?q?docs(course/update):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=20Zig=200.15.1=20=E5=8D=87=E7=BA=A7=E6=8C=87=E5=8D=97=E7=9A=84?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 0.15.1-description.md 中的冗余内容和重复说明 - 优化升级指南的章节组织和层级结构 - 简化示例代码并统一格式风格 - 提高文档的可读性和易用性 --- course/update/0.15.1-description.md | 700 --------------- course/update/upgrade-0.15.1.md | 1261 +++++++++------------------ 2 files changed, 390 insertions(+), 1571 deletions(-) diff --git a/course/update/0.15.1-description.md b/course/update/0.15.1-description.md index e408ddff..51b2376e 100644 --- a/course/update/0.15.1-description.md +++ b/course/update/0.15.1-description.md @@ -33,161 +33,6 @@ Zig 目前默认使用 x86 后端,调试编译速度提高了 **5** 倍;aarc packed union 字段现在不允许再单独指定 align 属性,这与 packed struct 的现有行为保持一致。此前即使为字段强制指定了对齐方式,也不会实际影响字段的对齐,这次迁移只需删去该部分即可。[#22997](https://github.com/ziglang/zig/pull/22997) -### 移除 `usingnamespace` - -该关键字增加了 `declaration` 的“预期”定义位置与“实际”定义位置之间的距离。在没有它的情况下,查找某个 `declaration` 的定义非常简单:只需找到你要查找的命名空间的定义,再在该类型 `declaration` 内部找到具体标识符即可。然而,有了 usingnamespace,程序员常常会被迫在不同类型和文件之间反复查找,寻觅声明的真实 `declaration` 位置。 - -这不仅影响了人类的可读性,对于工具链来说也带来了问题;例如,`Autodoc` 在遇到复杂的 `usingnamespace` 用法时无法准确分析(可以试着在 `0.14.1` 文档中查找 `std.c` 下的 `dl_iterate_phdr`)。 - -移除该特性后,所有标识符的来源都可以被轻松追溯到其实际导入的位置。 - -此外,`usingnamespace` 还会导致糟糕的命名空间划分。当声明被存放在单独的文件中时,通常意味着这些声明彼此之间有某些共同点,而与其他文件的内容无关。因此,将这些声明通过单独的命名空间进行暴露,而不是混入一个更通用的父级命名空间,其实是更合理的选择。简而言之:**好的命名空间结构是非常有价值的**。 - -最后,移除该特性让增量编译的实现变得根本上更加简单。 - -#### 示例:条件包含 - -`usingnamespace` 以前常用于有条件地包含声明,比如如下写法: - -```zig -pub usingnamespace if (have_foo) struct { - pub const foo = 123; -} else struct {}; -``` - -现在的替代办法其实非常简单:通常可以直接无条件地声明即可。Zig 拥有惰性编译特性,未被引用的声明不会被分析,因此这样不会有问题! - -```zig -pub const foo = 123; -``` - -但有时这种做法并不安全。例如,`foo` 虽然总能分析通过,但只有在 `have_foo` 为 true 时才有实际意义,在其他情况下引用则属于 bug。这种场景下,可以让未支持时声明直接在编译时报错: - -```zig -pub const foo = if (have_foo) - 123 -else - @compileError("foo not supported on this target"); -``` - -不过,这样做会破坏用 `@hasDecl` 进行特性检测的能力。如果确实需要特性检测,更推荐的写法是:将声明条件初始化为某种可检测的“哨兵值”(sentinel value),这样既不容易写错,也方便维护。常用的方案是使用 void 类型的 `{}`: - -```zig feature-detection.zig -const something = struct { - // 本例中,foo 被支持,bar 不被支持。 - const have_foo = true; - const have_bar = false; - pub const foo = if (have_foo) 123 else {}; - pub const bar = if (have_bar) undefined else {}; -}; - -test "use foo if supported" { - if (@TypeOf(something.foo) == void) return error.SkipZigTest; // 不支持 - try expect(something.foo == 123); -} - -test "use bar if supported" { - if (@TypeOf(something.bar) == void) return error.SkipZigTest; // 不支持 - try expect(something.bar == 456); -} - -const expect = @import("std").testing.expect; -``` - -#### 用例示例:多实现切换(Implementation Switching) - -`usingnamespace` 的另一个常见用途是根据不同条件(如平台目标)在编译期选择不同的实现体,如: - -```zig -pub usingnamespace switch (target) { - .windows => struct { - pub const target_name = "windows"; - pub fn init() T { - // ... - } - }, - else => struct { - pub const target_name = "something good"; - pub fn init() T { - // ... - } - }, -}; -``` - -替代方式其实更简单,推荐直接将定义本身写成条件表达式: - -```zig -pub const target_name = switch (target) { - .windows => "windows", - else => "something good", -}; -pub const init = switch (target) { - .windows => initWindows, - else => initOther, -}; -fn initWindows() T { - // ... -} -fn initOther() T { - // ... -} -``` - -#### 用例示例:Mixins 混入 - -使用 `usingnamespace` 的另一个常见场景,是实现“混入”特性(mixins): - -```zig -/// 提供操作 `count` 字段方法的混入体。 -pub fn CounterMixin(comptime T: type) type { - return struct { - pub fn incrementCounter(x: *T) void { - x.count += 1; - } - pub fn resetCounter(x: *T) void { - x.count = 0; - } - }; -} - -pub const Foo = struct { - count: u32 = 0, - pub usingnamespace CounterMixin(Foo); -}; -``` - -替代写法基于前述的命名空间思想:其实**恰当的命名空间划分是好事**。Mixins 也可以用命名空间来组织。比如把 `incrementCounter` 和 `resetCounter` 名称中的“counter”前缀直接变成一个嵌套字段,这样 `foo.counter.increment()` 替代 `foo.incrementCounter()`,语义更直观。 - -实现方式可利用零位字段(zero-bit field)和 `@fieldParentPtr`: - -```zig -/// 提供操作 `count` 字段方法的混入体。 -pub fn CounterMixin(comptime T: type) type { - return struct { - pub fn increment(m: *@This()) void { - const x: *T = @alignCast(@fieldParentPtr("counter", m)); - x.count += 1; - } - pub fn reset(m: *@This()) void { - const x: *T = @alignCast(@fieldParentPtr("counter", m)); - x.count = 0; - } - }; -} - -pub const Foo = struct { - count: u32 = 0, - counter: CounterMixin(Foo) = .{}, -}; -``` - -这样写后,调用就是 `foo.counter.increment()`,不再是 `foo.incrementCounter()`。通过零位字段成功将 mixin“命名空间化”。 - -实际上,这比以前更灵活,也能让混入体包含如字段等更多内容。比如本例中甚至可以将 `count` 字段本身都移到 `CounterMixin` 内部(虽然这种场景下其实不算 mixin,因为只剩状态本身)。 - -更重要的是,如果某些复杂混入需要额外状态,这种办法也能避免每个站点都重复字段声明,维护起来更方便。 - ### 移除 async 和 await 关键字 `async` 和 `await` 关键字已被移除,`@frameSize` 也已删除。 @@ -233,35 +78,6 @@ fn someOtherFunction(value: Enum) void { 布尔向量现在支持按位非、按位与、按位或、按位异或,以及布尔非等运算。 -### 内联汇编:类型化的 clobber 描述符 - -此前,clobber 描述符仅支持字符串类型。现在它们变为类型化的,实际用起来会更直观。 - -```zig -pub fn syscall1(number: usize, arg1: usize) usize { - return asm volatile ("syscall" - : [ret] "={rax}" (-> usize), - : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - : "rcx", "r11" - ); -} -``` - -⬇️ - -```zig -pub fn syscall1(number: usize, arg1: usize) usize { - return asm volatile ("syscall" - : [ret] "={rax}" (-> usize), - : [number] "{rax}" (number), - [arg1] "{rdi}" (arg1), - : .{ .rcx = true, .r11 = true }); -} -``` - -自动升级可使用 `zig fmt`。 - ### 允许 @ptrCast 从单项指针转换为切片 这是对 0.14.0 版本中 `@ptrCast` 支持切片长度转换特性的扩展。现在它还可以将单项指针转换为任意切片,返回一个引用与原始指针字节数相同的切片。 @@ -344,528 +160,12 @@ $ zig test lossy_int_to_float_coercion_new.zig All 1 tests passed. ``` -## 标准库 - -未归类的变更: - -- `fs.Dir.copyFile` 不再会因 `error.OutOfMemory` 失败 -- `fs.Dir.atomicFile` 现在要求在选项中带有 `write_buffer` -- `fs.AtomicFile` 现在有一个 `File.Writer` 字段,而不是 `File` 字段 -- `fs.File`:移除了 `WriteFileOptions`、`writeFileAll`、`writeFileAllUnseekable`,统一用 `File.Writer` 替代 -- `posix.sendfile` 移除,推荐使用 `fs.File.Reader.sendFile` - -### Writer 变动 - -[上一次相关变革(Allocgate)](https://ziglang.org/download/0.9.0/release-notes.html#Allocgate) - -所有现有的 `std.io` reader 和 writer 都已弃用,推荐使用全新的 `std.Io.Reader` 和 `std.Io.Writer`。这些新接口是非泛型(non-generic)的,缓冲区(buffer)在 vtable 之上。换句话说,缓冲区属于接口本身,而不是实现。这意味着 Reader 和 Writer 虽然不再是泛型类型,但在优化时依然透明:接口函数都针对 buffer 有明确的热路径,只有缓冲区满了才会触发 vtable 调用。 - -这些更改极其破坏性,对于此我表示歉意,但我已经仔细权衡,并对这个方向充满信心。希望你能系好安全带,和 Zig 一起前行,这会物有所值。 - -#### 变更动机 - -[Systems Distributed 2025 演讲:Don't Forget To Flush](https://www.youtube.com/watch?v=f30PceqQWko) - -- 老接口是泛型的,污染了包含它们的结构体,并导致所有相关函数都要用 `anytype` 泛型。新接口是具体类型。 - - 额外好处:类型具体化后,API 不容易直接作用于网络流、文件句柄或内存缓冲区,使代码更加可复用。例如更新后的 `http.Server` 不再依赖 `std.net`,而是仅操作流。 -- 老接口传递错误,但没有专用错误集合(error set),造成流的错误和 `anyerror` 类似。新接口为每个函数定义了明确的 error set,每个 error 都有可操作意义。 -- 新接口将缓冲区集成进接口本身,而不再用单独的 "BufferedReader"/"BufferedWriter" 抽象。这样对优化器更加友好,特别是在 debug 模式下。 -- 新接口支持诸如向量、填充(splatting)、直接文件传输(fd-to-fd)等高级特性,这些可以在读写器图(graph)中传递,减少系统调用、内存带宽和 CPU 使用率。 -- 新接口有 "peek" 功能,带来了用户 API 的便利与更简单易实现的实现逻辑。 - -#### 适配器 API - -如果你有旧流接口,需要转到新接口,可以这样写: - -```zig -fn foo(old_writer: anytype) !void { - var adapter = old_writer.adaptToNewApi(&.{}); - const w: *std.Io.Writer = &adapter.new_interface; - try w.print("{s}", .{"example"}); - // ... -} -``` - -#### 新的 `std.Io.Writer` 和 `std.Io.Reader` API - -这些环形缓冲区(ring buffer)有一组更方便、性能更好且非泛型的新 API。例如这样读取分隔符: - -```zig -while (reader.takeDelimiterExclusive('\n')) |line| { - // 处理每行... -} else |err| switch (err) { - error.EndOfStream, // 流未以换行结尾 - error.StreamTooLong, // 行过长,缓冲不下 - error.ReadFailed, // 可从实现中进一步诊断 - => |e| return e, -} -``` - -和其他语言的流实现相比,Zig 的流还新增了如下独特概念: - -- 读取时丢弃(discarding):可高效跳过数据。例如解压流在被请求丢弃大量数据时,可以直接跳过整个 frame。 -- 写入时填充(splatting):等同逻辑上的 `memset`,能在管道中无数据拷贝直接传递,将 O(M\*N) 操作变成 O(M),有时(比如写入零)还能优化到直接 seek 跳过。 -- 写入时传送整个文件:支持 I/O 管道在操作系统支持时直接 fd-to-fd 拷贝文件。 -- 用户负责提供缓冲区,流实现决定最小缓冲区大小,等于把部分状态从实现转移到用户所提供的 buffer 上。 - -#### `std.fs.File.Reader` 和 `std.fs.File.Writer` - -`std.fs.File.Reader` 会缓存(memoize)文件句柄的关键信息: - -- `stat` 得到的大小,或相关错误 -- 当前 seek 位置 -- 尝试 seek 时发生的错误 -- 是否应定位(positional)读取 -- 是否通过 fd-to-fd(如 `sendfile`)系统调用读取,亦或普通方式 - -该类型实现了 `std.Io.Reader` 接口。 - -实际用起来非常方便。有一个具体类型缓存文件大小等信息十分好用。大部分原本在文件句柄上操作 seek 的代码应迁移到这个 API,由于定位读取的优化,往往会变成无操作(no-op),同时仍支持回退到流式读。 - -`std.fs.File.Writer` 意义类似,用于写操作。 - -#### 升级 `std.io.getStdOut().writer().print()` - -务必使用缓冲!而且别忘记 flush! - -```zig -var stdout_buffer: [1024]u8 = undefined; -var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); -const stdout = &stdout_writer.interface; - -// ... - -try stdout.print("...", .{}); - -// ... - -try stdout.flush(); -``` - -#### `std.compress.flate` 重构 - -`std.compress` 的 API 全面重组了 flate,包括 zlib 和 gzip。现在主 API 是 `std.compress.flate.Decompress`,需要传 container 参数。 - -新 API 示例: - -```zig -var decompress_buffer: [std.compress.flate.max_window_len]u8 = undefined; -var decompress: std.compress.flate.Decompress = .init(reader, .zlib, &decompress_buffer); -const decompress_reader: *std.Io.Reader = &decompress.reader; -``` - -如果 `decompress_reader` 会被整个写入某个 `*Writer`,可以用空缓冲: - -```zig -var decompress: std.compress.flate.Decompress = .init(reader, .zlib, &.{}); -const n = try decompress.streamRemaining(writer); -``` - -压缩功能已被移除。如需相关逻辑,请直接复制老代码到你的应用,或用第三方包。 - -未来会把 deflate 加回 Zig 标准库。但目前语言进展优先于标准库,本次变更也是为最终锁定[以 I/O 为接口的设计](https://ziglang.org/download/0.15.1/release-notes.html#IO-as-an-Interface)做准备。 - -特别说明: - -- 新实现不再计算校验和(check sum),可由外部实现。 -- 新实现用普通 for 循环取代了之前复杂的匹配(match)逻辑。未来希望能有专用内存拷贝工具,zstd 也可用。即便如此,实际 untar 过程快 10%,编译器自身代码减小 2%(见 #24614)。 - -#### CountingWriter 已删除 - -- 需要丢弃字节时,请用 `std.Io.Writer.Discarding`,它支持计数。 -- 需要分配字节时,请用 `std.Io.Writer.Allocating`,它能查询已分配字节数。 -- 写入固定缓冲区时,用 `std.Io.Writer.fixed`,然后查询当前指针位置。 -- 或者,不要仅为计数字节而构建 stream graph 新节点,这会破坏高效缓冲。 - -#### BufferedWriter 已删除 - -```zig -const stdout_file = std.fs.File.stdout().writer(); -var bw = std.io.bufferedWriter(stdout_file); -const stdout = bw.writer(); - -try stdout.print("Run `zig build test` to run the tests.\n", .{}); - -try bw.flush(); // 别忘记 flush! -``` - -⬇️ - -```zig -var stdout_buffer: [4096]u8 = undefined; -var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); -const stdout = &stdout_writer.interface; - -try stdout.print("Run `zig build test` to run the tests.\n", .{}); - -try stdout.flush(); // 别忘记 flush! -``` - -建议将 `stdout` 缓冲区写成全局变量。 - -#### "{f}" 格式要求:必须显式调用 format 方法 - -开启 `-freference-trace` 可以帮助你排查所有 format 字符串问题。 - -示例: - -```zig -std.debug.print("{}", .{std.zig.fmtId("example")}); -``` - -现在会报如下编译错误: - -```sh -error: ambiguous format string; specify {f} to call format method, or {any} to skip it -``` - -修正为: - -```zig -std.debug.print("{f}", .{std.zig.fmtId("example")}); -``` - -动机:消除两个易踩坑点: - -- 向结构体添加 format 方法时,如果某处用了 `{}` 会导致行为悄然变化并引发 bug。 -- 移除 format 方法时,某处用 `{}` 也会在无提示下改变输出。 - -现在,如果给结构体加了 format 方法,所有用 `{}` 的地方会直接编译报错,强制你改用 `{f}` 或明确跳过。未来,这种更改不会影响已有代码输出。 - -同理,移除 format 方法后,所有用 `{}` 的地方行为不会再悄然变化。 - -总之,用 `{f}` 总是会尝试调用 format 方法,如果不存在,会报错。 - -#### format 方法不再接受格式字符串或选项参数 - -之前: - -```zig -pub fn format( - this: @This(), - comptime format_string: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { ... } -``` - -⬇️ - -现在: - -```zig -pub fn format(this: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } -``` - -被删掉的 `FormatOptions` 现只用于数字类型。 - -原来依赖格式字符串的代码可选三种改法: - -- 定义不同的格式化方法 - -```zig -pub fn formatB(foo: Foo, writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } -``` - -可用 `{f}`,配合 `. {std.fmt.alt(Foo, .formatB)}` 调用。 - -- 用 `std.fmt.Alt` - -```zig -pub fn bar(foo: Foo, context: i32) std.fmt.Alt(F, F.baz) { - return .{ .data = .{ .context = context } }; -} -const F = struct { - context: i32, - pub fn baz(f: F, writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } -}; -``` - -可用 `{f}`,`.{foo.bar(1234)}` 调用。 - -- 返回实现 format 方法的 struct,再配合 `{f}` 使用 - -```zig -pub fn bar(foo: Foo, context: i32) F { - return .{ .context = 1234 }; -} -const F = struct { - context: i32, - pub fn format(f: F, writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } -}; -``` - -同样支持 `{f}`,`.{foo.bar(1234)}` 调用。 - -#### 格式化打印不再支持 Unicode - -如果你的对齐依赖 Unicode 码点,现在仅支持 ASCII/字节。此前的实现未完全支持 Unicode。如需对齐 Unicode 字符串须用真正的 Unicode 库,标准库暂不支持。 - -#### 新格式化打印符号 - -- `{t}` 简写等同于 `@tagName()` 和 `@errorName()` -- `{d}` 及其它整数格式符可用于自定义类型,会调用 `formatNumber` 方法 -- `{b64}`:以标准 base64 输出字符串 - -### 链表类型去泛型 - -现在没必要自己手写 next/prev 指针,二进制体积也略小。 - -迁移方式: - -原来: - -```zig -std.DoublyLinkedList(T).Node -``` - -变为: - -```zig -struct { - node: std.DoublyLinkedList.Node, - data: T, -} -``` - -借助 `@fieldParentPtr` 可通过 node 找到 data。 - -许多场景更推荐把 node 直接嵌入数据结构(intrusive),如果还没这么做,很可能链表不是最佳选择。 - -#### std.Progress 支持进度条转义码 - -[终端支持进度状态的转义码](https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC)。 - -构建系统(Build System)的 `--watch` 能感知失败或成功并反馈终端,等待用户输入时也会清除进度条。 - -std.Progress 新增 `setStatus` 方法及如下枚举: - -```zig -pub const Status = enum { - /// 程序正在执行任务 - working, - /// 程序操作完成,等待用户输入(不直接 exit(0)) - success, - /// 程序发生错误,等待用户输入(不直接 exit(1)) - failure, - /// 程序有错误但还在工作 - failure_working, -}; -``` - -#### HTTP 客户端与服务端 - -相关 API 和实现已完全重写。 - -服务端 API 不再依赖 std.net,只依赖 std.Io.Reader / std.Io.Writer,且可以发无限数量的 header。 - -原本: - -```zig -var read_buffer: [8000]u8 = undefined; -var server = std.http.Server.init(connection, &read_buffer); -``` - -现在: - -```zig -var recv_buffer: [4000]u8 = undefined; -var send_buffer: [4000]u8 = undefined; -var conn_reader = connection.stream.reader(&recv_buffer); -var conn_writer = connection.stream.writer(&send_buffer); -var server = std.http.Server.init(conn_reader.interface(), &conn_writer.interface); -``` - -服务端和客户端都共享 `std.http.Reader` 与 `std.http.BodyWriter`,同样只依赖 I/O 流,不依赖网络。 - -客户端升级例子: - -原本: - -```zig -var server_header_buffer: [1024]u8 = undefined; -var req = try client.open(.GET, uri, .{ - .server_header_buffer = &server_header_buffer, -}); -defer req.deinit(); - -try req.send(); -try req.wait(); - -const body_reader = try req.reader(); -// read from body_reader... - -var it = req.response.iterateHeaders(); -while (it.next()) |header| { - _ = header.name; - _ = header.value; -} -``` - -现在: - -```zig -var req = try client.request(.GET, uri, .{}); -defer req.deinit(); - -try req.sendBodiless(); -var response = try req.receiveHead(&.{}); - -// 调用 reader() 后,response.head 内字符串无效 -var it = response.head.iterateHeaders(); -while (it.next()) |header| { - _ = header.name; - _ = header.value; -} - -// 具体缓冲区大小应根据 reader 用法选择 -var reader_buffer: [100]u8 = undefined; -const body_reader = response.reader(&reader_buffer); -``` - -### TLS 客户端 - -std.crypto.tls.Client 不再依赖 std.net 或 std.fs,仅依赖 std.Io.Reader / std.Io.Writer。 - -### ArrayList 默认无管理(unmanaged) - -- std.ArrayList -> std.array_list.Managed -- std.ArrayListAligned -> std.array_list.AlignedManaged - -注意:这两个类型最终都会被移除。 - -有 allocator 字段的 ArrayList 比没有更复杂,没必要。拥有 allocator 的 ArrayList 主要优势: - -- 避免用错 allocator -- 某些地方方便传递 allocator - -但缺点是: - -- 方法签名更复杂 -- 无法静态初始化 -- 更多内存占用(尤其嵌套容器时) - -最终:这些优点不值得。正确的 allocator 总是易得,也易于检查,只有一种实现更简单。实践中老用户对此很欢迎。 - -### 环形缓冲区(ring buffer) - -[标准库环形缓冲区实现太多了!](https://github.com/ziglang/zig/issues/19231) - -- `std.fifo.LinearFifo` 被移除——设计不佳,过度泛型,各种管理策略本质完全不同。 -- 绝大多数实际用途已被新 `std.Io.Writer` / `std.Io.Reader` API 覆盖。 -- `std.RingBuffer` 仅被 zstd 用,也已更换为新 API,被移除。 -- `std.compress.flate.CircularBuffer`(flate 内部用)已删除。 -- [HTTP Client 与 Server](https://ziglang.org/download/0.15.1/release-notes.html#HTTP-Client-and-Server) 也各有一个,也被删除并统一用新 API。 - -即便如此还会冒出环形缓冲区实现,比如: - -- lib/std/compress/lzma/decode/lzbuffer.zig(lzma 专用) -- lib/std/crypto/tls.zig —— 新 API 已替代 -- lib/std/debug/FixedBufferReader.zig —— 新 API 已替代 -- [某个随机 PR](https://github.com/ziglang/zig/issues/19231) - -玩笑归玩笑,未来标准库可能有通用可靠的 ring buffer,但请先问问你是否只需要 `std.Io.Reader` 或 `std.Io.Writer`。 - -### BoundedArray 被移除 - -这种结构因易于拷贝而流行,但带来隐藏成本。 - -升级建议,根据容量上限来源分类: - -- 如果是任意值,仅作为能力上限建议,不要猜。让调用方传入缓冲区 slice 或用动态分配。(例:#24699 里的 markdown) -- 如果只是栈上有限容量的类型安全,直接用 `ArrayListUnmanaged`。很方便!(例:#24699 的 test_switch_dispatch_loop.zig) - -`std.ArrayList` 现在所有 "AssumeCapacity" 的方法都有 "Bounded" 变体: - -原本: - -```zig -var stack = try std.BoundedArray(i32, 8).fromSlice(initial_stack); -``` - -现在: - -```zig -var buffer: [8]i32 = undefined; -var stack = std.ArrayListUnmanaged(i32).initBuffer(&buffer); -try stack.appendSliceBounded(initial_stack); -``` - -- 如果是有固定最大容量的有序集合,这很少见,直接自己编码管理就好。(例:#24699 Zcu.zig) -- 如果是用作可复制的可增长数组?那会无谓地到处复制未定义内存和增加泛型膨胀,建议不用。 - -### 删除和弃用内容 - -- std.fs.File.reader -> std.fs.File.deprecatedReader -- std.fs.File.writer -> std.fs.File.deprecatedWriter -- std.fmt.fmtSliceEscapeLower -> std.ascii.hexEscape -- std.fmt.fmtSliceEscapeUpper -> std.ascii.hexEscape -- std.zig.fmtEscapes -> std.zig.fmtString -- std.fmt.fmtSliceHexLower -> {x} -- std.fmt.fmtSliceHexUpper -> {X} -- std.fmt.fmtIntSizeDec -> {B} -- std.fmt.fmtIntSizeBin -> {Bi} -- std.fmt.fmtDuration -> {D} -- std.fmt.fmtDurationSigned -> {D} -- std.fmt.Formatter -> std.fmt.Alt - - 现在需显式指定 context 类型 - - 无 fmt 字符串参数 -- std.fmt.format -> std.Io.Writer.print -- std.io.GenericReader -> std.Io.Reader -- std.io.GenericWriter -> std.Io.Writer -- std.io.AnyReader -> std.Io.Reader -- std.io.AnyWriter -> std.Io.Writer -- 删除 std.io.SeekableStream - - 请用 *std.fs.File.Reader、*std.fs.File.Writer 或具体的 std.ArrayListUnmanaged,根据场景选取实现方式 -- 删除 std.io.BitReader - - 不建议此层面抽象位读取,会妨碍热循环的性能优化,请与流实现紧密耦合 -- 删除 std.io.BitWriter - - 同上 -- 删除 std.Io.LimitedReader -- 删除 std.Io.BufferedReader -- 删除 std.fifo - ## 构建系统 未归类的变更: - zig build: 在构建总结前输出一个换行 -### 移除旧的 root_module 字段 - -Zig 0.14.0 新增了 `root_module` 字段到 `std.Build.ExecutableOptions` 及相关 API,同时废弃了此前用来指定主模块的字段 `root_source_file` 等。在 Zig 0.15.x 中,这些旧字段已被彻底移除。如果你没有在上一版本周期内迁移代码,现在会遇到类似如下的编译错误: - -```zig deprecated-addExecutable.zig -pub fn build(b: *std.Build) void { - const exe = b.addExecutable(.{ - .name = "foo", - .root_source_file = b.path("src/main.zig"), - .target = b.graph.host, - .optimize = .Debug, - }); - b.installArtifact(exe); -} - -test { - _ = &build; -} -const std = @import("std"); -``` - -```sh Shell -$ zig test deprecated-addExecutable.zig -src/download/0.15.1/release-notes/deprecated-addExecutable.zig:4:10: error: no field named 'root_source_file' in struct 'Build.ExecutableOptions' - .root_source_file = b.path("src/main.zig"), - ^~~~~~~~~~~~~~~~ -/home/ci/deps/zig-x86_64-linux-0.15.1/lib/std/Build.zig:771:31: note: struct declared here -pub const ExecutableOptions = struct { - ^~~~~~ -referenced by: - test_0: src/download/0.15.1/release-notes/deprecated-addExecutable.zig:12:10 -``` - -关于如何迁移旧代码,请参考 [Zig 0.14.0 发布说明对应章节](https://ziglang.org/download/0.14.0/release-notes.html#Creating-Artifacts-from-Existing-Modules)。 - ### macOS 文件系统监听 现在,`zig build` 的 `--watch` 参数已支持 macOS 系统。在 Zig 0.14.0 时,这个参数虽然可用,但对大多数编辑器表现异常;而在 Zig 0.15.x 里,这一功能已被[重新实现](https://github.com/ziglang/zig/pull/24649),采用了 macOS 的 File System Events API,确保文件系统变更监听快速且可靠。 diff --git a/course/update/upgrade-0.15.1.md b/course/update/upgrade-0.15.1.md index 1924d1f1..c3a01234 100644 --- a/course/update/upgrade-0.15.1.md +++ b/course/update/upgrade-0.15.1.md @@ -5,1138 +5,657 @@ showVersion: false 本篇文档将介绍如何从 `0.14.0` 版本升级到 `0.15.1`。 -## 语言变更 +## 语言变动 ### 移除 `usingnamespace` -`usingnamespace` 关键字已被移除。以下是常见用例的替代方案: +`usingnamespace` 关键字已被完全移除。需要将其替换为更明确的声明方式。 -#### 用例:条件包含 +#### 条件包含 + +旧写法: ```zig -// 旧代码 -const builtin = @import("builtin"); -const os_impl = switch (builtin.os.tag) { - .linux => @import("os/linux.zig"), - .windows => @import("os/windows.zig"), - else => @compileError("Unsupported OS"), -}; -usingnamespace os_impl; +pub usingnamespace if (have_foo) struct { + pub const foo = 123; +} else struct {}; ``` -⬇️ +新写法: ```zig -// 新代码 -const builtin = @import("builtin"); -const os_impl = switch (builtin.os.tag) { - .linux => @import("os/linux.zig"), - .windows => @import("os/windows.zig"), - else => @compileError("Unsupported OS"), -}; +// 方案 1:直接声明(推荐) +pub const foo = 123; + +// 方案 2:使用 compileError +pub const foo = if (have_foo) + 123 +else + @compileError("foo not supported on this target"); -// 显式导出所需函数 -pub const open = os_impl.open; -pub const close = os_impl.close; -pub const read = os_impl.read; -pub const write = os_impl.write; +// 方案 3:使用哨兵值支持特性检测 +pub const foo = if (have_foo) 123 else {}; ``` -#### 用例:实现切换 +#### 多实现切换 + +旧写法: ```zig -// 旧代码 -const MyStruct = struct { - data: u32, - - usingnamespace if (builtin.mode == .Debug) - @import("debug_impl.zig") - else - @import("release_impl.zig"); +pub usingnamespace switch (target) { + .windows => struct { + pub const target_name = "windows"; + pub fn init() T { + // ... + } + }, + else => struct { + pub const target_name = "something good"; + pub fn init() T { + // ... + } + }, }; ``` -⬇️ +新写法: ```zig -// 新代码 -const Impl = if (builtin.mode == .Debug) - @import("debug_impl.zig") -else - @import("release_impl.zig"); - -const MyStruct = struct { - data: u32, - - pub fn doSomething(self: *MyStruct) void { - return Impl.doSomething(self); - } - - // 显式实现所有需要的方法 +pub const target_name = switch (target) { + .windows => "windows", + else => "something good", +}; +pub const init = switch (target) { + .windows => initWindows, + else => initOther, }; +fn initWindows() T { + // ... +} +fn initOther() T { + // ... +} ``` -#### 用例:混入 +#### Mixins 混入 + +旧写法: ```zig -// 旧代码 -fn LoggingMixin(comptime T: type) type { +pub fn CounterMixin(comptime T: type) type { return struct { - pub fn log(self: *T, message: []const u8) void { - std.log.info("{s}: {s}", .{ @typeName(T), message }); + pub fn incrementCounter(x: *T) void { + x.count += 1; + } + pub fn resetCounter(x: *T) void { + x.count = 0; } }; } -const MyStruct = struct { - data: u32, - usingnamespace LoggingMixin(@This()); +pub const Foo = struct { + count: u32 = 0, + pub usingnamespace CounterMixin(Foo); }; ``` -⬇️ +新写法(使用零位字段): ```zig -// 新代码 -fn LoggingMixin(comptime T: type) type { +pub fn CounterMixin(comptime T: type) type { return struct { - pub fn log(self: *T, message: []const u8) void { - std.log.info("{s}: {s}", .{ @typeName(T), message }); + pub fn increment(m: *@This()) void { + const x: *T = @alignCast(@fieldParentPtr("counter", m)); + x.count += 1; + } + pub fn reset(m: *@This()) void { + const x: *T = @alignCast(@fieldParentPtr("counter", m)); + x.count = 0; } }; } -const MyStruct = struct { - data: u32, - - const Mixin = LoggingMixin(@This()); - - pub fn log(self: *MyStruct, message: []const u8) void { - Mixin.log(self, message); - } +pub const Foo = struct { + count: u32 = 0, + counter: CounterMixin(Foo) = .{}, }; + +// 使用方式 +// foo.counter.increment() 替代 foo.incrementCounter() ``` -### 移除 `async` 和 `await` 关键字 +### 非穷尽枚举的 switch 改进 -`async` 和 `await` 关键字已被移除。这为未来引入新的异步编程方案做准备。 +现在可以在非穷尽枚举的 switch 中组合使用显式标签和 `_` 分支: ```zig -// 旧代码 - 将不再工作 -fn asyncFunction() async void { - // 异步代码 -} - -fn caller() void { - const frame = async asyncFunction(); - await frame; +switch (enum_val) { + .special_case_1 => foo(), + .special_case_2 => bar(), + _, .special_case_3 => baz(), } ``` -目前需要使用其他方式处理并发,等待未来版本中新的异步编程支持。 - -### 对非穷尽枚举的 `switch` 支持 - -现在可以对非穷尽枚举使用 `switch` 语句: +注意:不能同时使用 `else` 和 `_`: ```zig -const MyEnum = enum(u8) { - foo = 1, - bar = 2, - _, +const Enum = enum(u32) { + A = 1, + B = 2, + C = 44, + _ }; -fn handleEnum(value: MyEnum) void { +fn someFunction(value: Enum) void { + // 错误:不能同时使用 else 和 _ switch (value) { - .foo => std.debug.print("foo\n", .{}), - .bar => std.debug.print("bar\n", .{}), - else => std.debug.print("unknown\n", .{}), + .A => {}, + .C => {}, + else => {}, // 处理已命名但未列出的标签 + _ => {}, // 处理未命名标签 } } ``` -### 允许对布尔向量使用更多运算符 +### 内联汇编:类型化的 clobber 描述符 -布尔向量现在支持更多运算符: +旧写法: ```zig -test "boolean vector operations" { - const vec_a: @Vector(4, bool) = .{ true, false, true, false }; - const vec_b: @Vector(4, bool) = .{ false, true, true, false }; - - const and_result = vec_a & vec_b; - const or_result = vec_a | vec_b; - const xor_result = vec_a ^ vec_b; - const not_result = ~vec_a; +pub fn syscall1(number: usize, arg1: usize) usize { + return asm volatile ("syscall" + : [ret] "={rax}" (-> usize), + : [number] "{rax}" (number), + [arg1] "{rdi}" (arg1), + : "rcx", "r11" + ); } ``` -### 内联汇编:类型化的破坏描述符 - -内联汇编现在支持类型化的 clobber 列表: +新写法: ```zig -// 旧代码 -asm volatile ("mov %[src], %[dst]" - : [dst] "=r" (dst) - : [src] "r" (src) - : "memory" // 字符串形式 -); +pub fn syscall1(number: usize, arg1: usize) usize { + return asm volatile ("syscall" + : [ret] "={rax}" (-> usize), + : [number] "{rax}" (number), + [arg1] "{rdi}" (arg1), + : .{ .rcx = true, .r11 = true }); +} ``` -⬇️ +可以使用 `zig fmt` 自动升级。 -```zig -// 新代码 -asm volatile ("mov %[src], %[dst]" - : [dst] "=r" (dst) - : [src] "r" (src) - : .memory // 类型化形式 -); -``` +### @ptrCast 从单项指针转换为切片 -### 允许 `@ptrCast` 从单项指针到切片的转换 - -现在可以使用 `@ptrCast` 将单项指针转换为切片: +现在 `@ptrCast` 可以将单项指针转换为切片: ```zig -test "ptrCast to slice" { - var value: u32 = 42; - const ptr: *u32 = &value; - - // 新功能:将单项指针转换为切片 - const slice: []u32 = @ptrCast(ptr); - - std.testing.expect(slice[0] == 42); -} +const val: u32 = 1; +const bytes: []const u8 = @ptrCast(&val); ``` -### 对 `undefined` 进行算术运算的新规则 +注意:未来计划将此功能移至 `@memCast`。 -`undefined` 值的算术运算规则发生了变化: - -```zig -test "undefined arithmetic" { - const a: i32 = undefined; - const b: i32 = 10; - - // 任何与 undefined 的运算都返回 undefined - const result = a + b; // result 是 undefined - _ = result; - - // 更安全的做法 - const safe_a: i32 = 0; // 显式初始化 - const safe_result = safe_a + b; - std.testing.expect(safe_result == 10); -} -``` +### undefined 上的算术操作 -### 对从整数到浮点数的有损转换发出错误 +只有永远不会导致非法行为的运算符才允许 `undefined` 作为操作数。 -现在对于可能导致精度损失的转换会发出编译错误: +错误示例: ```zig -test "lossy integer to float conversion" { - const large_int: u64 = 0x1FFFFFFFFFFFFF; // 53 位 - - // 这将产生编译错误,因为 f64 只有 52 位尾数 - // const float_val: f64 = @floatFromInt(large_int); // 错误! - - // 需要显式使用有损转换 - const float_val: f64 = @floatFromInt(@as(u53, @truncate(large_int))); - - std.debug.print("Float: {}\n", .{float_val}); -} +const a: u32 = 0; +const b: u32 = undefined; +_ = a + b; // 错误:对 undefined 的使用导致非法行为 ``` -## 标准库 - -### Writergate - -这是本次更新中最重大的破坏性更改,原有的 `std.io` 中的读写器接口被完全重新设计。 +最佳实践:**始终避免对 undefined 进行任何操作**。 -#### 动机 +### 整数到浮点的损失性转换 -旧的 I/O 系统存在以下问题: +编译时整数转换为浮点数时,如果无法精确表示,现在会报错: -- 泛型接口导致编译时间过长 -- 缓冲区管理不一致 -- 性能不佳,存在不必要的内存拷贝 -- 接口复杂,难以优化 - -#### 适配器 API - -新的设计引入了适配器模式: +错误示例: ```zig -// 旧的泛型接口 -fn processData(reader: anytype, writer: anytype) !void { - var buffer: [1024]u8 = undefined; - while (true) { - const bytes_read = try reader.read(&buffer); - if (bytes_read == 0) break; - try writer.writeAll(buffer[0..bytes_read]); - } -} +const val: f32 = 123_456_789; // 错误:f32 无法表示此整数值 ``` -⬇️ +修复方法: ```zig -// 新的适配器接口 -fn processData(reader: std.Io.Reader, writer: std.Io.Writer) !void { - var buffer: [1024]u8 = undefined; - while (true) { - const bytes_read = try reader.read(&buffer); - if (bytes_read == 0) break; - try writer.writeAll(buffer[0..bytes_read]); - } -} - -// 使用时需要适配器 -const file = try std.fs.cwd().openFile("input.txt", .{}); -defer file.close(); - -const stdout = std.io.getStdOut(); - -try processData( - file.reader().adapter(), // 适配器转换 - stdout.writer().adapter(), // 适配器转换 -); +const val: f32 = 123_456_789.0; // 使用浮点字面量 ``` -### 新的 `std.Io.Writer` 和 `std.Io.Reader` API - -新的 API 采用**非泛型设计**: - -```zig -// std.Io.Writer 的定义 -pub const Writer = struct { - ptr: *anyopaque, - vtable: *const VTable, - - pub const VTable = struct { - write: *const fn (*anyopaque, []const u8) anyerror!usize, - writeAll: *const fn (*anyopaque, []const u8) anyerror!void, - writeByte: *const fn (*anyopaque, u8) anyerror!void, - }; - - pub fn write(self: Writer, bytes: []const u8) !usize { - return self.vtable.write(self.ptr, bytes); - } +## 标准库 - pub fn writeAll(self: Writer, bytes: []const u8) !void { - return self.vtable.writeAll(self.ptr, bytes); - } -}; -``` +### Writer 和 Reader 重大变更 -#### `std.fs.File.Reader` 和 `std.fs.File.Writer` +这是 0.15.1 最大的破坏性变更。所有 `std.io` 的 reader 和 writer 都已弃用,需要迁移到新的 `std.Io.Reader` 和 `std.Io.Writer`。 -文件 I/O 的使用方式也发生了变化: +#### 主要变化 -```zig -// 旧代码 -const file = try std.fs.cwd().openFile("data.txt", .{}); -defer file.close(); +1. **新接口是非泛型的**:不再使用 `anytype` +2. **缓冲区在接口内部**:不需要单独的 BufferedReader/Writer +3. **明确的错误集合**:每个函数都有具体的 error set +4. **新增高级特性**:支持向量、填充、直接文件传输等 -const reader = file.reader(); -var buffer: [1024]u8 = undefined; -const bytes_read = try reader.read(&buffer); -``` +#### 适配旧代码 -⬇️ +如果你有旧的 writer,可以使用适配器: ```zig -// 新代码 -const file = try std.fs.cwd().openFile("data.txt", .{}); -defer file.close(); - -// 方式 1:直接使用文件方法 -var buffer: [1024]u8 = undefined; -const bytes_read = try file.read(&buffer); - -// 方式 2:使用新的 Reader 接口 -const reader = file.reader(); -const io_reader = reader.adapter(); -const bytes_read2 = try io_reader.read(&buffer); +fn foo(old_writer: anytype) !void { + var adapter = old_writer.adaptToNewApi(&.{}); + const w: *std.Io.Writer = &adapter.new_interface; + try w.print("{s}", .{"example"}); +} ``` -#### 升级 `std.io.getStdOut().writer().print()` +#### 升级 stdout -标准输出的使用方式也需要调整: +旧写法: ```zig -// 旧代码 const stdout = std.io.getStdOut().writer(); -try stdout.print("Hello, {s}!\n", .{"World"}); +try stdout.print("...", .{}); ``` -⬇️ +新写法: ```zig -// 新代码 -// 方式 1:使用 std.debug.print(推荐) -std.debug.print("Hello, {s}!\n", .{"World"}); - -// 方式 2:使用新的 Writer 接口 -const stdout = std.io.getStdOut(); -const writer = stdout.writer().adapter(); -try writer.print("Hello, {s}!\n", .{"World"}); - -// 方式 3:直接使用文件方法 -const stdout = std.io.getStdOut(); -try stdout.writeAll("Hello, World!\n"); -``` +var stdout_buffer: [1024]u8 = undefined; +var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); +const stdout = &stdout_writer.interface; -#### 重构 `std.compress.flate` +try stdout.print("...", .{}); -压缩相关的代码也需要大量修改: - -```zig -// 旧代码 -var deflate_stream = std.compress.flate.deflateStream(allocator, writer); -defer deflate_stream.deinit(); -try deflate_stream.writer().writeAll(data); -try deflate_stream.finish(); +try stdout.flush(); // 别忘记 flush! ``` -⬇️ - -```zig -// 新代码 -var deflate_stream = std.compress.flate.DeflateStream.init(allocator, writer.adapter()); -defer deflate_stream.deinit(); -try deflate_stream.writeAll(data); -try deflate_stream.finish(); -``` - -#### 删除 `CountingWriter` - -`CountingWriter` 被删除,需要手动计数: +#### std.fs.File.Reader 和 Writer -```zig -// 旧代码 -var counting_writer = std.io.countingWriter(base_writer); -``` - -⬇️ +新的 `std.fs.File.Reader` 会缓存文件信息: ```zig -// 新代码 - 需要手动计数 -var bytes_written: usize = 0; +var file_reader = file.reader(&buffer); +const reader: *std.Io.Reader = &file_reader.interface; ``` -#### 删除 `BufferedWriter` +#### compress.flate 重构 -`BufferedWriter` 被重构: +旧写法: ```zig -// 旧代码 -var buffered_writer = std.io.bufferedWriter(base_writer); +var decompress = try std.compress.flate.decompressor(allocator, reader, null); +defer decompress.deinit(); ``` -⬇️ +新写法: ```zig -// 新代码 -var buffer: [4096]u8 = undefined; -var buffered_writer = std.io.BufferedWriter(4096, @TypeOf(base_writer)).init(base_writer); +var decompress_buffer: [std.compress.flate.max_window_len]u8 = undefined; +var decompress: std.compress.flate.Decompress = .init(reader, .zlib, &decompress_buffer); +const decompress_reader: *std.Io.Reader = &decompress.reader; + +// 如果要直接写入 writer,可以使用空缓冲: +var decompress: std.compress.flate.Decompress = .init(reader, .zlib, &.{}); +const n = try decompress.streamRemaining(writer); ``` -### 调用 `format` 方法需要使用 `{f}` +**注意**:压缩功能已被移除。 -现在调用自定义的 `format` 方法需要使用 `{f}` 格式说明符: +#### CountingWriter 已删除 -```zig -const Point = struct { - x: f32, - y: f32, +根据需求选择替代方案: - pub fn format(self: Point, writer: anytype) !void { - try writer.print("Point({d}, {d})", .{ self.x, self.y }); - } -}; +- 丢弃字节:使用 `std.Io.Writer.Discarding` +- 分配字节:使用 `std.Io.Writer.Allocating` +- 固定缓冲区:使用 `std.Io.Writer.fixed` -// 使用时必须使用 {f} -const point = Point{ .x = 1.0, .y = 2.0 }; -std.debug.print("{f}\n", .{point}); // 注意使用 {f} -``` +#### BufferedWriter 已删除 -### `format` 方法不再使用格式字符串或选项 - -格式化方法的签名已经简化: +旧写法: ```zig -// 旧代码 -const Point = struct { - x: f32, - y: f32, - - pub fn format( - self: Point, - comptime fmt: []const u8, // 被移除 - options: std.fmt.FormatOptions, // 被移除 - writer: anytype, - ) !void { - _ = fmt; - _ = options; - try writer.print("Point({d}, {d})", .{ self.x, self.y }); - } -}; -``` +const stdout_file = std.fs.File.stdout().writer(); +var bw = std.io.bufferedWriter(stdout_file); +const stdout = bw.writer(); -⬇️ - -```zig -// 新代码 -const Point = struct { - x: f32, - y: f32, - - pub fn format( - self: Point, - writer: anytype, // 只保留 writer 参数 - ) !void { - try writer.print("Point({d}, {d})", .{ self.x, self.y }); - } -}; +try stdout.print("...\n", .{}); +try bw.flush(); ``` -### 格式化打印不再处理 Unicode - -Unicode 处理被移除,需要手动处理: +新写法: ```zig -// 旧代码 -std.debug.print("你好,{s}!\n", .{"世界"}); // 自动处理 Unicode -``` - -⬇️ +var stdout_buffer: [4096]u8 = undefined; +var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); +const stdout = &stdout_writer.interface; -```zig -// 新代码 - 需要手动处理 Unicode -const message = "你好,世界!"; -std.debug.print("{s}\n", .{message}); // 仍然可以显示,但不保证所有 Unicode 处理 +try stdout.print("...\n", .{}); +try stdout.flush(); ``` -### 新的格式化打印说明符 - -新增了一些格式化说明符: +### 格式化打印变更 -```zig -const value: u32 = 255; - -// 新的格式化选项 -std.debug.print("{d}\n", .{value}); // 十进制:255 -std.debug.print("{x}\n", .{value}); // 十六进制:ff -std.debug.print("{X}\n", .{value}); // 大写十六进制:FF -std.debug.print("{o}\n", .{value}); // 八进制:377 -std.debug.print("{b}\n", .{value}); // 二进制:11111111 - -// 浮点数格式化 -const pi: f64 = 3.14159; -std.debug.print("{d:.2}\n", .{pi}); // 保留两位小数:3.14 -std.debug.print("{e}\n", .{pi}); // 科学计数法:3.14159e+00 - -// 指针和切片 -const ptr: *u32 = &value; -const slice: []const u8 = "hello"; -std.debug.print("{*}\n", .{ptr}); // 指针地址 -std.debug.print("{s}\n", .{slice}); // 字符串 -std.debug.print("{any}\n", .{slice}); // 通用格式化 -``` +#### "{f}" 必须显式调用 format 方法 -### 去泛型化链表 - -链表类型也进行了重构: +旧写法: ```zig -// 旧代码 -const Node = std.SinglyLinkedList(i32).Node; -var list = std.SinglyLinkedList(i32){}; -var node = Node{ .data = 42 }; -list.prepend(&node); +std.debug.print("{}", .{std.zig.fmtId("example")}); ``` -⬇️ +新写法: ```zig -// 新代码 -const SinglyLinkedList = std.SinglyLinkedList; -const Node = SinglyLinkedList.Node; - -var list: SinglyLinkedList = .{}; -var node = Node{ .data = @as(*anyopaque, @ptrCast(&@as(i32, 42))) }; -list.prepend(&node); - -// 或者使用新的 API -var typed_list = std.DoublyLinkedList(i32){}; -var typed_node = std.DoublyLinkedList(i32).Node{ .data = 42 }; -typed_list.append(&typed_node); +std.debug.print("{f}", .{std.zig.fmtId("example")}); ``` -### `std.Progress` 支持进度条转义码 - -进度条系统得到了增强,支持更多的终端转义码。 +使用 `-freference-trace` 可以帮助排查所有格式字符串问题。 -### HTTP 客户端和服务器 +#### format 方法签名变更 -标准库新增了 HTTP 支持: +旧签名: ```zig -// HTTP 客户端示例 -const std = @import("std"); - -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); - - var client = std.http.Client{ .allocator = allocator }; - defer client.deinit(); - - const uri = std.Uri.parse("https://httpbin.org/get") catch unreachable; - - var headers = std.http.Headers{ .allocator = allocator }; - defer headers.deinit(); - - try headers.append("User-Agent", "Zig HTTP Client"); - - var request = try client.open(.GET, uri, headers, .{}); - defer request.deinit(); - - try request.send(.{}); - try request.wait(); - - const body = try request.reader().readAllAlloc(allocator, 8192); - defer allocator.free(body); - - std.debug.print("Response: {s}\n", .{body}); -} +pub fn format( + this: @This(), + comptime format_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { ... } ``` -### TLS 客户端 - -内置 TLS 支持: +新签名: ```zig -// TLS 连接示例 -const std = @import("std"); - -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); - - const address = try std.net.Address.parseIp("93.184.216.34", 443); // example.com - const stream = try std.net.tcpConnectToAddress(address); - defer stream.close(); - - var tls_client = try std.crypto.tls.Client.init(stream, .{ - .host = "example.com", - .allocator = allocator, - }); - defer tls_client.deinit(); - - const request = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"; - try tls_client.writeAll(request); - - var buffer: [4096]u8 = undefined; - const bytes_read = try tls_client.read(&buffer); - std.debug.print("Response: {s}\n", .{buffer[0..bytes_read]}); -} +pub fn format(this: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } ``` -### `ArrayList`:将非托管模式设为默认 +如果需要不同的格式化方法,有三种方案: -这是一个重大的破坏性更改。`std.ArrayList` 现在默认采用 **非托管 (unmanaged)** 模式: +1. 定义不同的格式化方法: ```zig -// 旧代码 -const std = @import("std"); - -fn oldArrayListExample(allocator: std.mem.Allocator) !void { - var list = std.ArrayList(i32).init(allocator); - defer list.deinit(); - - try list.append(42); - try list.appendSlice(&[_]i32{ 1, 2, 3 }); - - for (list.items) |item| { - std.debug.print("{d} ", .{item}); - } -} +pub fn formatB(foo: Foo, writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } +// 使用:.{std.fmt.alt(Foo, .formatB)} ``` -⬇️ +2. 使用 `std.fmt.Alt`: ```zig -// 新代码 -const std = @import("std"); - -fn newArrayListExample(allocator: std.mem.Allocator) !void { - // 方式 1:使用新的 ArrayListUnmanaged - var list: std.ArrayListUnmanaged(i32) = .empty; - defer list.deinit(allocator); - - try list.append(allocator, 42); - try list.appendSlice(allocator, &[_]i32{ 1, 2, 3 }); - - for (list.items) |item| { - std.debug.print("{d} ", .{item}); - } -} - -// 方式 2:使用别名保持兼容性 -fn compatibilityExample(allocator: std.mem.Allocator) !void { - // ArrayList 现在是 ArrayListUnmanaged 的别名 - const ArrayList = std.ArrayListUnmanaged; - var list: ArrayList(i32) = .empty; - defer list.deinit(allocator); - - try list.append(allocator, 42); -} - -// 方式 3:使用新的初始化方法 -fn newInitExample(allocator: std.mem.Allocator) !void { - var list = std.ArrayList(i32).init(allocator); // 仍然可用,但已弃用 - defer list.deinit(); - - // 或者使用新的方式 - var new_list: std.ArrayList(i32) = .{}; - defer new_list.deinit(allocator); - - try new_list.append(allocator, 42); +pub fn bar(foo: Foo, context: i32) std.fmt.Alt(F, F.baz) { + return .{ .data = .{ .context = context } }; } ``` -其他容器类型也发生了类似的变化: +3. 返回实现 format 的结构: ```zig -// HashMap -var map: std.HashMapUnmanaged([]const u8, i32, std.hash_map.StringContext, 80) = .{}; -defer map.deinit(allocator); - -try map.put(allocator, "key", 42); -const value = map.get("key"); - -// ArrayHashMap -var array_map: std.ArrayHashMapUnmanaged([]const u8, i32, std.hash_map.StringContext, false) = .{}; -defer array_map.deinit(allocator); - -try array_map.put(allocator, "key", 42); - -// PriorityQueue -var pq: std.PriorityQueueUnmanaged(i32, void, compareInts) = .{}; -defer pq.deinit(allocator); - -try pq.add(allocator, 42); - -fn compareInts(context: void, a: i32, b: i32) std.math.Order { - _ = context; - return std.math.order(a, b); +pub fn bar(foo: Foo, context: i32) F { + return .{ .context = 1234 }; } +const F = struct { + context: i32, + pub fn format(f: F, writer: *std.Io.Writer) std.Io.Writer.Error!void { ... } +}; ``` -### 环形缓冲区 +#### 格式化打印不再支持 Unicode -新增了环形缓冲区类型: +对齐功能现在仅支持 ASCII/字节,不再支持 Unicode 码点。 -```zig -const RingBuffer = std.RingBuffer; - -// 创建环形缓冲区 -var buffer: [16]u8 = undefined; -var ring = RingBuffer.init(&buffer); +#### 新格式化符号 -// 写入数据 -const data = "Hello, World!"; -const written = ring.write(data); -std.debug.print("Written {} bytes\n", .{written}); - -// 读取数据 -var read_buffer: [32]u8 = undefined; -const read_count = ring.read(&read_buffer); -std.debug.print("Read: {s}\n", .{read_buffer[0..read_count]}); -``` +- `{t}` - 等同于 `@tagName()` 和 `@errorName()` +- `{d}` - 可用于自定义类型(需实现 `formatNumber` 方法) +- `{b64}` - 以标准 base64 输出字符串 -### 移除 `BoundedArray` +### 链表类型去泛型 -`BoundedArray` 被移除,建议使用其他替代方案: +旧写法: ```zig -// 旧代码 -var bounded = std.BoundedArray(i32, 10).init(0); -try bounded.append(42); +std.DoublyLinkedList(T).Node ``` -⬇️ +新写法: ```zig -// 新代码 - 使用 ArrayList 或数组 -var list: std.ArrayListUnmanaged(i32) = .empty; -defer list.deinit(allocator); - -// 或者使用固定大小数组 -var array: [10]i32 = undefined; -var count: usize = 0; - -if (count < array.len) { - array[count] = 42; - count += 1; +struct { + node: std.DoublyLinkedList.Node, + data: T, } ``` -### 删除和弃用 +使用 `@fieldParentPtr` 可通过 node 找到 data。 -本次更新中删除和弃用了多个 API,需要使用新的替代方案。 - -## 构建系统 +### std.Progress 支持进度条 -### 移除已弃用的隐式根模块 - -构建系统中已移除对隐式根模块的支持,需要在构建脚本中明确指定模块: +`std.Progress` 新增 `setStatus` 方法: ```zig -// 旧的 build.zig -pub fn build(b: *std.Build) void { - const exe = b.addExecutable(.{ - .name = "app", - .root_source_file = b.path("src/main.zig"), - .target = target, - .optimize = optimize, - }); -} -``` - -⬇️ - -```zig -// 新的 build.zig -pub fn build(b: *std.Build) void { - const exe = b.addExecutable(.{ - .name = "app", - .root_module = b.createModule(.{ - .root_source_file = b.path("src/main.zig"), - .target = target, - .optimize = optimize, - }), - }); -} -``` - -### macOS 文件系统监视 - -在 macOS 上,构建系统现在支持文件系统监视功能,能够在文件更改时自动触发重建: - -```bash -# 启用文件系统监视 -zig build --watch - -# 设置去抖动时间(默认50ms) -zig build --watch --debounce 100 +pub const Status = enum { + working, // 正在执行任务 + success, // 操作完成,等待用户输入 + failure, // 发生错误,等待用户输入 + failure_working, // 有错误但还在工作 +}; ``` -### Web 界面和即时报告 - -构建系统现在支持 Web 界面和详细的即时报告功能。 - -## 编译器 - -### x86 后端 +### HTTP 客户端与服务端重构 -在 **调试编译** 默认为 **x86 后端** 的情况下,其速度提高了 5 倍。这也是本次更新最显著的性能改进: +#### 服务端 API -```bash -# 使用新的 x86 后端(默认) -zig build -Doptimize=Debug +旧写法: -# 如果需要使用 LLVM 后端 -zig build -Doptimize=Debug -fLLVM +```zig +var read_buffer: [8000]u8 = undefined; +var server = std.http.Server.init(connection, &read_buffer); ``` -### aarch64 后端 - -**aarch64 后端** 的开发也在稳步推进,为 ARM 平台提供更好的支持。 - -### 增量编译 +新写法: -增量编译功能得到了改进: - -```bash -# 启用增量编译 -zig build -fincremental - -# 结合文件系统监视 -zig build -fincremental --watch - -# 仅检查编译错误(不生成二进制文件) -zig build -fincremental -fno-emit-bin --watch +```zig +var recv_buffer: [4000]u8 = undefined; +var send_buffer: [4000]u8 = undefined; +var conn_reader = connection.stream.reader(&recv_buffer); +var conn_writer = connection.stream.writer(&send_buffer); +var server = std.http.Server.init(conn_reader.interface(), &conn_writer.interface); ``` -### 多线程代码生成 +使用流程: -编译器现在支持多线程代码生成: +```zig +var server = std.http.Server.init(conn_reader.interface(), &conn_writer.interface); +var request = try server.receiveHead(); +const body_reader = try request.reader(); +// 读取 body... -```bash -# 控制编译线程数 -zig build -j4 # 使用 4 个线程 -zig build -j # 使用所有可用 CPU 核心 +// 一次性响应 +try request.respond(content, .{ + .status = status, + .extra_headers = &.{ + .{ .name = "content-type", .value = "text/html" }, + }, +}); -# 禁用多线程编译 -zig build -j1 +// 或流式响应 +var response = try request.respondStreaming(); +const body_writer = response.writer(); +// 写入 body... +try response.end(); ``` -### 允许在模块级别配置 UBSan 模式 +#### 客户端 API -现在可以在模块级别配置 UBSan(未定义行为检测器): +旧写法: ```zig -// build.zig 中的配置 -const exe = b.addExecutable(.{ - .name = "app", - .root_module = b.createModule(.{ - .root_source_file = b.path("src/main.zig"), - .target = target, - .optimize = optimize, - .sanitize_c = true, // 启用 C 代码的 UBSan - }), +var server_header_buffer: [1024]u8 = undefined; +var req = try client.open(.GET, uri, .{ + .server_header_buffer = &server_header_buffer, }); +defer req.deinit(); -// 或者使用命令行 -zig build -Dsanitize-c -``` - -### 将测试编译为目标文件 - -现在可以将测试编译为目标文件而不是可执行文件: +try req.send(); +try req.wait(); -```bash -# 将测试编译为 .o 文件 -zig test src/main.zig --object +const body_reader = try req.reader(); +// 读取 body... -# 将测试编译为静态库 -zig test src/main.zig --library - -# 将测试编译为动态库 -zig test src/main.zig --dynamic-library +var it = req.response.iterateHeaders(); +while (it.next()) |header| { + // 处理 header +} ``` -### Zig 初始化 - -`zig init` 命令得到了改进: - -```bash -# 创建新项目 -zig init +新写法: -# 创建库项目 -zig init --lib +```zig +var req = try client.request(.GET, uri, .{}); +defer req.deinit(); -# 创建可执行项目 -zig init --exe +try req.sendBodiless(); +var response = try req.receiveHead(&.{}); -# 指定项目名称 -zig init --name my-project +// 先处理 headers +var it = response.head.iterateHeaders(); +while (it.next()) |header| { + // 处理 header +} -# 在现有目录中初始化 -zig init . +// 再读取 body +var reader_buffer: [100]u8 = undefined; +const body_reader = response.reader(&reader_buffer); ``` -## 链接器 +### TLS 客户端 -链接器得到了多项改进和优化。 +`std.crypto.tls.Client` 不再依赖 `std.net` 或 `std.fs`,仅依赖 `std.Io.Reader` / `std.Io.Writer`。 -## 模糊测试器 +### ArrayList 默认无管理(unmanaged) -Zig 0.15.1 集成了一个内置的模糊测试器: +旧的有管理版本已弃用: -```bash -# 启动模糊测试 -zig build test --fuzz +- `std.ArrayList` -> `std.array_list.Managed`(将被移除) +- `std.ArrayListAligned` -> `std.array_list.AlignedManaged`(将被移除) -# 指定端口 -zig build test --fuzz --port 8080 - -# 指定测试时间 -zig build test --fuzz --timeout 60 -``` +推荐使用: ```zig -// 模糊测试例子 -test "fuzz string parsing" { - const input_bytes = std.testing.fuzzInput(.{}); - - // 测试字符串解析函数 - const result = parseString(input_bytes); - - // 确保不会崩溃 - _ = result catch |err| { - // 预期的错误可以忽略 - if (err == error.InvalidInput) return; - return err; - }; -} - -fn parseString(input: []const u8) ![]const u8 { - if (input.len == 0) return error.InvalidInput; - // 解析逻辑... - return input; -} +const ArrayList = std.ArrayListUnmanaged; +var list: ArrayList(i32) = .empty; +defer list.deinit(gpa); +try list.append(gpa, 1234); +try list.ensureUnusedCapacity(gpa, 1); +list.appendAssumeCapacity(5678); ``` -## 错误修复 - -本次发布修复了大量错误。 - -## 本次发布包含的错误 +新的 "Bounded" 变体方法: -Zig 目前仍存在一些已知的 bugs,包括一些编译错误。 - -## 工具链 - -### LLVM 20 - -本次更新升级到 LLVM 20,带来了以下改进: - -- 更好的优化支持 -- 新的目标架构支持 -- 改进的调试信息生成 -- 更好的 C++ 交互性 - -### 在交叉编译时支持动态链接的 FreeBSD libc - -```bash -# 交叉编译到 FreeBSD -zig build -Dtarget=x86_64-freebsd-gnu -zig build -Dtarget=aarch64-freebsd-gnu - -# 使用动态链接的 libc -zig build -Dtarget=x86_64-freebsd-gnu -Ddynamic-linker +```zig +var buffer: [8]i32 = undefined; +var stack = std.ArrayListUnmanaged(i32).initBuffer(&buffer); +try stack.appendSliceBounded(initial_stack); ``` -### 在交叉编译时支持动态链接的 NetBSD libc +### BoundedArray 被移除 -```bash -# 交叉编译到 NetBSD -zig build -Dtarget=x86_64-netbsd-gnu -zig build -Dtarget=aarch64-netbsd-gnu +根据场景选择替代方案: -# 使用 NetBSD 系统 libc -zig cc -target x86_64-netbsd-gnu program.c -``` - -### glibc 2.42 +1. 任意容量上限 -> 让调用方传入缓冲区或用动态分配 +2. 栈上有限容量 -> 直接用 `ArrayListUnmanaged` -升级到 glibc 2.42,并支持静态链接本地 glibc: +示例: -```bash -# 使用系统 glibc 静态链接 -zig build -Dtarget=native-linux-gnu -Dlinkage=static +旧写法: -# 指定 glibc 版本 -zig build -Dtarget=x86_64-linux-gnu.2.42 +```zig +var stack = try std.BoundedArray(i32, 8).fromSlice(initial_stack); ``` -#### 允许静态链接本机 glibc - -现在可以静态链接本机系统的 glibc。 - -### MinGW-w64 - -MinGW-w64 工具链得到了更新: - -```bash -# Windows 交叉编译 -zig build -Dtarget=x86_64-windows-gnu -zig build -Dtarget=aarch64-windows-gnu +新写法: -# 使用 MinGW-w64 工具链 -zig cc -target x86_64-windows-gnu program.c +```zig +var buffer: [8]i32 = undefined; +var stack = std.ArrayListUnmanaged(i32).initBuffer(&buffer); +try stack.appendSliceBounded(initial_stack); ``` -### zig libc - -`zig libc` 命令得到了增强: - -```bash -# 查看当前目标的 libc 配置 -zig libc - -# 查看指定目标的 libc -zig libc -target x86_64-linux-gnu +### 删除和弃用内容 -# 查看所有可用的 libc -zig targets | grep libc -``` - -### zig cc +以下 API 已被删除或重命名: -`zig cc` 作为 C 编译器的功能得到了增强: +```zig +// 文件操作 +std.fs.File.reader -> std.fs.File.deprecatedReader +std.fs.File.writer -> std.fs.File.deprecatedWriter -```bash -# 更好的 GCC 兼容性 -zig cc -std=c11 -O2 program.c +// 格式化 +std.fmt.fmtSliceEscapeLower -> std.ascii.hexEscape +std.fmt.fmtSliceEscapeUpper -> std.ascii.hexEscape +std.zig.fmtEscapes -> std.zig.fmtString +std.fmt.fmtSliceHexLower -> {x} +std.fmt.fmtSliceHexUpper -> {X} +std.fmt.fmtIntSizeDec -> {B} +std.fmt.fmtIntSizeBin -> {Bi} +std.fmt.fmtDuration -> {D} +std.fmt.fmtDurationSigned -> {D} +std.fmt.Formatter -> std.fmt.Alt +std.fmt.format -> std.Io.Writer.print -# 支持更多编译选项 -zig cc -march=native -mtune=native program.c +// IO +std.io.GenericReader -> std.Io.Reader +std.io.GenericWriter -> std.Io.Writer +std.io.AnyReader -> std.Io.Reader +std.io.AnyWriter -> std.Io.Writer +std.io.SeekableStream -> 删除(用具体实现替代) +std.io.BitReader -> 删除 +std.io.BitWriter -> 删除 +std.Io.LimitedReader -> 删除 +std.Io.BufferedReader -> 删除 +std.fifo -> 删除 -# 更好的错误报告 -zig cc -Wall -Wextra program.c +// POSIX +std.posix.iovec.iov_base -> .base +std.posix.iovec.iov_len -> .len ``` -### zig objcopy 回归 - -`zig objcopy` 命令存在一些回归问题,正在修复中。 - -## 路线图 - -### I/O 作为接口 - -I/O 系统将继续作为接口进行发展和完善。 - -## 升级建议 - -由于此次更新包含多项破坏性更改,建议开发者在升级至 0.15.1 版本时: - -### 升级步骤 - -1. **备份现有代码**:在开始升级之前做好备份 +### std.c 重组 -2. **分步骤进行**: - - 首先处理 `usingnamespace` 的移除 - - 然后处理 I/O 系统的重构 - - 最后处理 ArrayList 等容器的变更 +`std.c` 现在有更好的组织结构,并且消除了最后一个 `usingnamespace` 的使用。 -3. **使用编译器检查**: - - ```bash - # 检查编译错误 - zig build-exe src/main.zig +## 构建系统 - # 使用新的 x86 后端加速编译 - zig build -fno-LLVM - ``` +### 移除旧的 root_module 字段 -4. **测试全面性**:确保所有功能在新版本下正常运行 -5. **利用新特性**: - - 使用新的 x86 后端提高编译速度 - - 使用文件系统监视功能 - - 尝试增量编译 +`root_source_file` 等字段已被完全移除。 -### 常见问题和解决方案 +错误示例: -1. **I/O 代码编译错误**:参考上面的 Writergate 部分 -2. **`usingnamespace` 错误**:使用显式导入替代 -3. **ArrayList 错误**:改为使用 ArrayListUnmanaged -4. **格式化错误**:更新 format 方法签名 +```zig +const exe = b.addExecutable(.{ + .name = "foo", + .root_source_file = b.path("src/main.zig"), // 错误 + .target = b.graph.host, + .optimize = .Debug, +}); +``` -## 注意事项 +正确写法: -- 这次升级的破坏性更改较多,建议在测试环境中进行充分测试 -- I/O 接口的重构可能需要较多的代码调整工作 -- 新的性能改进主要体现在编译速度上 -- 文件系统监视功能有助于提高开发效率 +```zig +const exe = b.addExecutable(.{ + .name = "foo", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), +}); +``` -完整的发布说明可在 [Zig 官方网站](https://ziglang.org/download/0.15.1/release-notes.html) 查看。 +参考 [0.14.0 升级指南](./upgrade-0.14.0.md#从现有模块创建工件)。 From 03f6d21f6387c2444560698dd5417ab4b8b2a8c9 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Wed, 22 Oct 2025 14:51:48 +0800 Subject: [PATCH 07/11] =?UTF-8?q?docs(course/basic):=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E5=AE=9A=E4=B9=89=E6=96=87=E6=A1=A3=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=20usingnamespace=20=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 usingnamespace 关键字的详细说明 - 删除相关的代码示例和使用演示 - 移除关于概念学习困难的提示说明 --- course/basic/define-variable.md | 43 --------------------------------- 1 file changed, 43 deletions(-) diff --git a/course/basic/define-variable.md b/course/basic/define-variable.md index 0cad1cf0..9ff8fbd0 100644 --- a/course/basic/define-variable.md +++ b/course/basic/define-variable.md @@ -141,49 +141,6 @@ PS: 说实话,我认为这个设计并不太好。 为什么是作用域顶层呢?实际上,Zig 将一个源码文件看作是一个容器。 ::: -## `usingnamespace` - -关键字 `usingnamespace` 可以将一个容器中的所有 `pub` 声明混入到当前的容器中。 - -例如,可以使用 `usingnamespace` 将 `std` 标准库混入到 `main.zig` 这个容器中: - -```zig -const T = struct { - usingnamespace @import("std"); -}; -pub fn main() !void { - T.debug.print("Hello, World!\n", .{}); -} -``` - -注意:无法在结构体 `T` 内部直接使用混入的声明,需要使用 `T.debug` 这种方式才可以! - -`usingnamespace` 还可以使用 `pub` 关键字进行修饰,用于转发声明,这常用于组织 API 文件和 C 语言的 `import`。 - -```zig -pub usingnamespace @cImport({ - @cInclude("epoxy/gl.h"); - @cInclude("GLFW/glfw3.h"); - @cDefine("STBI_ONLY_PNG", ""); - @cDefine("STBI_NO_STDIO", ""); - @cInclude("stb_image.h"); -}); -``` - -相关的使用方法可以是这样的: - -```zig -pub usingnamespace @cImport({ - @cInclude("xcb/xcb.h"); - @cInclude("xcb/xproto.h"); -}); -``` - -针对以上引入的头文件,我们可以这样使用 `@This().xcb_generic_event_t`。 - -> [!IMPORTANT] -> 初次阅读此处感到困惑是正常的。在学习完后续概念后,此处内容将自然理解。 - ## `threadlocal` 变量可以使用 `threadlocal` 修饰符,使得该变量在不同线程中拥有不同的实例: From e8df00ab0bcf43c919c5f6217952baafa5b24186 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Wed, 22 Oct 2025 19:11:54 +0800 Subject: [PATCH 08/11] =?UTF-8?q?refactor(course/code/15):=20=E5=B0=86=20s?= =?UTF-8?q?td=20=E5=AF=BC=E5=85=A5=E6=9C=AC=E5=9C=B0=E5=8C=96=E5=88=B0?= =?UTF-8?q?=E5=90=84=E4=B8=AA=E7=BB=93=E6=9E=84=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除全局作用域中的 std 模块导入 - 在 One、Two、Three 结构体内部分别添加 std 导入 - 改进代码作用域和模块化组织 --- course/code/15/hello_world.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/course/code/15/hello_world.zig b/course/code/15/hello_world.zig index 19eec76e..9c1a521b 100644 --- a/course/code/15/hello_world.zig +++ b/course/code/15/hello_world.zig @@ -1,5 +1,3 @@ -const std = @import("std"); - pub fn main() !void { try One.main(); try Two.main(); @@ -8,6 +6,7 @@ pub fn main() !void { const One = struct { // #region one + const std = @import("std"); pub fn main() !void { std.debug.print("Hello, World!\n", .{}); } @@ -16,6 +15,7 @@ const One = struct { const Two = struct { // #region two + const std = @import("std"); pub fn main() !void { var stdout_buffer: [1024]u8 = undefined; var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); @@ -35,6 +35,7 @@ const Two = struct { const Three = struct { // #region three + const std = @import("std"); pub fn main() !void { // 定义两个缓冲区 var stdout_buffer: [1024]u8 = undefined; // [!code focus] From 28045055325f638fd7950e5ef9a03a868c145823 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Wed, 22 Oct 2025 19:14:16 +0800 Subject: [PATCH 09/11] =?UTF-8?q?docs(course/hello-world):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=BA=BF=E7=A8=8B=E5=AE=89=E5=85=A8=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=9A=84=E6=96=87=E6=A1=A3=E6=8E=92=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将线程安全内容拆分为多个独立段落 - 分离问题定义、解决方案和建议 - 提高文档的清晰度和可读性 --- course/hello-world.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/course/hello-world.md b/course/hello-world.md index b410fe1b..f2152503 100644 --- a/course/hello-world.md +++ b/course/hello-world.md @@ -81,9 +81,13 @@ Zig 本身没有内置的 `@print()` 函数,输出功能通常由标准库的 ## 更进一步:线程安全 -以上代码在单线程环境下工作良好,但在多线程环境中,多个线程同时调用 `print` 可能会导致输出内容交错混乱。为了保证线程安全,我们需要为 `writer` 添加锁。 +以上代码在单线程环境下工作良好,但在多线程环境中,多个线程同时调用 `print` 可能会导致输出内容交错混乱。 -你可以使用 `std.Thread.Mutex` 来实现一个线程安全的 `writer`。我们鼓励你阅读[标准库源码](https://ziglang.org/documentation/master/std/#std.Thread.Mutex)来深入了解其工作原理。 +为了保证线程安全,我们需要为 `writer` 添加锁。 + +你可以使用 `std.Thread.Mutex` 来实现一个线程安全的 `writer`。 + +我们鼓励你阅读[标准库源码](https://ziglang.org/documentation/master/std/#std.Thread.Mutex)来深入了解其工作原理。 ## 了解更多 From f0155f50f9ab1df223dceafb9672bf1afc3a7687 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Wed, 22 Oct 2025 19:30:13 +0800 Subject: [PATCH 10/11] =?UTF-8?q?docs(project):=20=E4=B8=BA=20AI=20?= =?UTF-8?q?=E5=8A=A9=E6=89=8B=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E8=A7=84=E8=8C=83=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 记录项目结构、开发工作流和代码示例组织方式 - 说明构建系统机制、CI/CD 流水线配置和编码规范 - 提供常见开发任务操作指南和技术栈信息 --- AGENTS.md | 426 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 426 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..356505e2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,426 @@ +# AGENTS.md + +本文档帮助 AI 助手理解 Zig 语言圣经项目的结构和开发规范。 + +## 项目概述 + +**Zig 语言圣经** 是一个全面的开源 Zig 编程语言中文教程。项目旨在为中文开发者提供高质量的 Zig 学习资源。 + +- **官方网站**: https://course.ziglang.cc/ +- **GitHub Pages**: https://zigcc.github.io/zig-course/ +- **代码仓库**: https://github.com/zigcc/zig-course +- **开源协议**: MIT +- **主要语言**: 简体中文 +- **文档语言**: 中文为主,技术术语保留英文 + +## 核心目标 + +1. 提供全面的 Zig 编程语言中文文档 +2. 支持多个 Zig 版本(0.11 至 0.16) +3. 维护可运行的代码示例 +4. 记录版本升级指南和破坏性变更 +5. 构建高质量的中文 Zig 社区学习资源 + +## 项目结构 + +``` +zig-course/ +├── build.zig # 主构建编排器 +├── build/ # 版本特定的构建脚本 +│ ├── 0.11.zig # Zig 0.11 构建逻辑 +│ ├── 0.12.zig # Zig 0.12 构建逻辑 +│ ├── 0.13.zig # Zig 0.13 构建逻辑 +│ ├── 0.14.zig # Zig 0.14 构建逻辑 +│ ├── 0.15.zig # Zig 0.15 构建逻辑(当前重点) +│ └── 0.16.zig # Zig 0.16 构建逻辑 +│ +├── course/ # 主要文档内容 +│ ├── .vitepress/ # VitePress 配置 +│ │ ├── config.mts # 站点主配置 +│ │ ├── sidebar.ts # 侧边栏导航结构 +│ │ ├── nav.ts # 顶部导航 +│ │ └── theme/ # 自定义主题组件 +│ │ +│ ├── environment/ # 环境搭建指南 +│ ├── basic/ # Zig 基础概念 +│ ├── advanced/ # 进阶主题 +│ ├── engineering/ # 软件工程实践 +│ ├── examples/ # 实战示例 +│ ├── update/ # 版本升级指南 +│ ├── appendix/ # 参考资料 +│ │ +│ ├── code/ # 可运行的代码示例 +│ │ ├── 11/ # Zig 0.11 示例 +│ │ ├── 12/ # Zig 0.12 示例 +│ │ ├── 13/ → ./12 # 符号链接(与 0.12 兼容) +│ │ ├── 14/ # Zig 0.14 示例 +│ │ ├── 15/ # Zig 0.15 示例(当前活跃版本) +│ │ └── release/ → ./15 # 符号链接到最新稳定版 +│ │ +│ ├── picture/ # 图片资源 +│ └── public/ # 静态网站资源 +│ +├── .github/ +│ └── workflows/ # CI/CD 流水线 +│ ├── build.yml # 多平台、多版本构建 +│ ├── deploy.yml # GitHub Pages 部署 +│ ├── check.yml # 代码格式检查 +│ ├── autocorrect.yml # 中文文本格式检查 +│ └── pdf.yml # PDF 导出流程 +│ +├── package.json # Node.js 依赖(Bun) +├── bun.lock # Bun 锁文件 +└── flake.nix # Nix 开发环境 +``` + +## 开发工作流 + +### 构建和测试 + +项目使用多版本构建系统确保跨 Zig 版本兼容性: + +```bash +# 使用当前 Zig 版本构建所有示例 +zig build + +# 主 build.zig 会根据检测到的 Zig 编译器版本 +# 自动分发到 build/ 目录下对应的版本特定脚本 +``` + +### 文档开发 + +文档站点使用 VitePress 构建: + +```bash +# 启动带热重载的开发服务器 +bun dev + +# 构建生产站点 +bun build + +# 预览构建后的站点 +bun preview + +# 导出 PDF 版本 +bun export-pdf +``` + +### 代码格式化 + +```bash +# 格式化所有代码(Markdown、Zig、中文文本) +bun format + +# 检查格式但不修改文件 +bun check +``` + +这会运行: +- Prettier 处理 Markdown/TypeScript/JavaScript +- `zig fmt` 处理 Zig 源文件 +- AutoCorrect 处理中文文本格式 + +## 关键文件及其用途 + +| 文件/目录 | 用途 | +|-----------|------| +| `build.zig` | 主构建编排器,检测 Zig 版本并委托给对应的构建脚本 | +| `build/0.15.zig` | 当前活跃的构建脚本,具有智能示例发现功能 | +| `course/.vitepress/config.mts` | VitePress 配置(语言、主题、SEO) | +| `course/.vitepress/sidebar.ts` | 定义课程导航结构 | +| `course/code/15/` | Zig 0.15 可运行示例(当前活跃版本) | +| `course/basic/` | Zig 基础概念(变量、类型、控制流) | +| `course/advanced/` | 进阶主题(comptime、异步、内存管理) | +| `course/update/` | 版本迁移指南 | +| `package.json` | 构建脚本和 Node.js 依赖 | +| `.github/workflows/build.yml` | 跨平台和版本的 CI/CD 测试 | + +## 代码示例组织方式 + +示例按 Zig 版本组织在 `course/code/` 目录下: + +### 结构模式 + +1. **独立文件**:简单示例作为单个 `.zig` 文件 + - 示例:`course/code/15/array.zig`、`course/code/15/comptime.zig` + - 构建脚本自动发现并编译这些文件 + +2. **子项目**:复杂示例拥有自己的 `build.zig` + - 示例:`course/code/15/build_system/`、`course/code/15/package_management_importer/` + - 构建脚本递归调用子构建 + +### 添加新示例 + +添加新示例时: + +1. **简单概念**:在 `course/code/15/` 创建独立 `.zig` 文件 +2. **复杂功能**:创建带有 `build.zig` 的子目录 +3. **更新文档**:在相应章节添加对应的 `.md` 文件 +4. **确保编译**:运行 `zig build` 验证 +5. **格式化代码**:提交前运行 `bun format` + +## 文档结构 + +课程遵循渐进式学习路径: + +1. **环境搭建**(`course/environment/`) + - 安装指南 + - 编辑器配置 + - Zig CLI 参考 + +2. **基础学习**(`course/basic/`) + - 变量和类型 + - 控制流 + - 错误处理 + - 类型系统基础 + +3. **进阶学习**(`course/advanced/`) + - 编译期计算(comptime) + - 异步/await + - 内存管理 + - C 互操作 + - 反射和元编程 + +4. **工程实践**(`course/engineering/`) + - 构建系统 + - 包管理 + - 单元测试 + - 代码风格指南 + +5. **实战示例**(`course/examples/`) + - 完整项目(如 TCP 服务器) + +6. **版本指南**(`course/update/`) + - 版本发布说明 + - 迁移指南 + +## 构建系统内部机制 + +### 主构建脚本(`build.zig`) + +主构建脚本: +1. 检测当前 Zig 编译器版本 +2. 分发到 `build/` 目录下对应的版本特定构建脚本 +3. 优雅处理版本兼容性 + +### 版本特定构建脚本(`build/0.15.zig`) + +每个版本构建脚本: +1. **发现示例**:扫描 `course/code/VERSION/` 目录 +2. **分类**: + - 独立 `.zig` 文件 → 编译为可执行文件 + - 包含 `build.zig` 的目录 → 作为子项目调用 +3. **平台处理**:应用平台特定配置 +4. **测试执行**:运行内嵌的单元测试 + +### 示例发现算法 + +``` +对于 course/code/VERSION/ 中的每个项目: + 如果项目是 .zig 文件: + → 创建可执行文件构建步骤 + 否则如果项目是目录: + 如果目录包含 build.zig: + → 创建对子项目构建的依赖 + 否则: + → 跳过(可能是支持文件) +``` + +## CI/CD 流水线 + +### 构建工作流(`.github/workflows/build.yml`) + +- **触发器**:`.zig` 文件、构建脚本或配置文件变更 +- **矩阵**:3 个操作系统(Ubuntu、macOS、Windows)× 6 个 Zig 版本 +- **目的**:确保所有示例在各平台和版本上都能编译 +- **调度**:每日运行 + +### 部署工作流(`.github/workflows/deploy.yml`) + +- **触发器**:推送到 `main` 分支 +- **目的**:构建并部署文档到 GitHub Pages +- **步骤**: + 1. 安装 Bun + 2. 安装依赖 + 3. 构建 VitePress 站点 + 4. 上传到 GitHub Pages + +### 检查工作流(`.github/workflows/check.yml`) + +- **触发器**:Pull Request、推送 +- **目的**:验证代码格式 +- **工具**:Prettier、Zig fmt、AutoCorrect + +## 编码规范 + +### 提交信息 + +遵循 Conventional Commits: +``` +(): + +类型: feat, fix, docs, refactor, chore, test, style +作用域: course/basic, course/advanced, build, ci 等 + +示例: +- feat(course/basic): 为 0.15 添加数组示例 +- fix(build): 修正平台检测逻辑 +- docs(course/update): 更新 0.15.1 迁移指南 +- refactor(course/code/15): 简化 comptime 示例 +``` + +### 分支命名 + +``` +feature/ # 新功能 +fix/ # Bug 修复 +docs/ # 文档更新 +update/ # 版本更新 + +示例: update/fix-0.15.1 +``` + +### 代码风格 + +**Zig 代码**: +- 使用 `zig fmt`(在 CI 中强制执行) +- 遵循 Zig 风格指南原则 +- 为公共 API 添加文档注释 + +**Markdown**: +- 中文文本使用中文标点(。,!?) +- 中文和英文/数字之间添加空格 +- 代码块使用具体语言标记:` ```zig`,不要用 ` ``` +- 图片使用相对路径:`![描述](../picture/...)` + +**中文文本**: +- 正确使用标点符号(。不是 .) +- 正确使用引号(「」不是 "") +- 中英文之间加空格 +- 由 CI 中的 AutoCorrect 检查 + +### 文件组织 + +**文档文件**: +- 放在 `course/` 下的适当分类中 +- 使用英文描述性文件名 +- 如添加新页面需更新 `course/.vitepress/sidebar.ts` + +**代码示例**: +- 放在 `course/code/15/`(或当前版本) +- 尽可能匹配文档结构 +- 包含解释概念的文档注释 + +**图片**: +- 放在 `course/picture/` 中 +- 使用子目录组织 +- 提交前优化图片 + +## 常见任务 + +### 添加新的语言概念 + +1. **创建文档**:`course/basic/.md` 或 `course/advanced/.md` +2. **添加代码示例**:`course/code/15/.zig` +3. **更新侧边栏**:在 `course/.vitepress/sidebar.ts` 添加条目 +4. **测试编译**:运行 `zig build` +5. **格式化**:运行 `bun format` +6. **提交**:使用规范的提交信息 + +### 为新 Zig 版本更新 + +1. **创建新构建脚本**:`build/0.XX.zig`(从上一版本复制并修改) +2. **复制示例**:`cp -r course/code/15 course/code/XX` +3. **更新符号链接**:`ln -sf XX course/code/release` +4. **修复破坏性变更**:根据需要更新代码示例 +5. **记录变更**:创建 `course/update/upgrade-0.XX.md` +6. **更新 CI**:在 `.github/workflows/build.yml` 矩阵中添加新版本 +7. **彻底测试**:确保所有示例都能编译 + +### 修复文档问题 + +1. **定位文件**:使用 grep 或文件搜索 +2. **编辑内容**:保持中文文档风格 +3. **预览**:运行 `bun dev` 查看变更 +4. **格式化**:运行 `bun format` +5. **提交**:使用 `docs(): ` 格式 + +### 添加复杂示例 + +1. **创建目录**:`course/code/15//` +2. **添加 build.zig**:创建适当的构建配置 +3. **添加源文件**:实现示例 +4. **添加 README**:解释示例演示的内容 +5. **更新文档**:在相关 `.md` 文件中引用示例 +6. **测试**:从项目根目录运行 `zig build` + +## 版本兼容性策略 + +项目使用以下方式维护跨 Zig 版本兼容性: + +1. **独立代码目录**:每个主要版本有自己的示例 +2. **版本特定构建脚本**:处理语法/API 差异 +3. **符号链接**:用于兼容版本(如 0.13 → 0.12) +4. **迁移指南**:记录破坏性变更和升级路径 +5. **CI 矩阵测试**:通过自动化测试确保兼容性 + +## 技术栈 + +- **语言**:Zig(0.11 - 0.16) +- **文档**:VitePress(Vue 3、TypeScript) +- **包管理器**:Bun(也可使用 npm/yarn) +- **CI/CD**:GitHub Actions +- **托管**:GitHub Pages +- **格式化**:Prettier、Zig fmt、AutoCorrect + +## AI 助手重要注意事项 + +### 处理文档时 + +1. **语言**:使用简体中文编写,使用正确的标点符号 +2. **代码块**:始终指定语言:` ```zig`,绝不使用纯 ` ``` +3. **格式化**:中文文本应使用中文标点(。而非 .) +4. **间距**:在中文字符和英文/数字之间添加空格 +5. **链接**:内部文档使用相对路径 + +### 处理代码示例时 + +1. **版本**:除非另有说明,目标版本为 Zig 0.15 +2. **位置**:添加到 `course/code/15/` +3. **测试**:确保代码能编译和运行 +4. **文档**:代码配合解释性文档 +5. **注释**:为公共 API 使用文档注释(`///`) + +### 修改构建系统时 + +1. **主 build.zig**:很少需要更改(处理版本分发) +2. **版本脚本**:修改适当的 `build/0.XX.zig` +3. **发现逻辑**:理解自动示例检测机制 +4. **平台处理**:考虑 Linux、macOS、Windows 差异 + +### 更新 CI/CD 时 + +1. **构建矩阵**:保持版本矩阵更新 +2. **缓存**:保留 Zig 编译器缓存以提高速度 +3. **平台测试**:确保跨平台兼容性 +4. **部署工作流**:仅在 main 分支运行 + +## 资源链接 + +- [Zig 官方文档](https://ziglang.org/documentation/) +- [VitePress 文档](https://vitepress.dev/) +- [Conventional Commits](https://www.conventionalcommits.org/) +- [贡献指南](CONTRIBUTING.md) +- [行为准则](CODE_OF_CONDUCT.md) + +## 项目统计 + +- **文档页面**:约 482 个 Markdown 文件 +- **代码示例**:约 235 个 Zig 源文件 +- **支持版本**:6 个(Zig 0.11 - 0.16) +- **构建配置**:6 个版本特定构建脚本 +- **CI 测试矩阵**:18 种组合(3 个操作系统 × 6 个版本) + +--- + +最后更新:2025-10-22(由 AI 助手自动生成) From c91a8db163668e12c4ee92680336c4b45b9f8818 Mon Sep 17 00:00:00 2001 From: jinzhongjia Date: Thu, 23 Oct 2025 13:40:46 +0800 Subject: [PATCH 11/11] =?UTF-8?q?chore(project):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E7=BB=9F=E8=AE=A1=E9=83=A8=E5=88=86=E5=B9=B6?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20CLAUDE.md=20=E7=AC=A6=E5=8F=B7=E9=93=BE?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 AGENTS.md 中的项目统计信息章节 - 创建 CLAUDE.md 符号链接指向 AGENTS.md - 优化文档组织结构 --- AGENTS.md | 8 -------- CLAUDE.md | 1 + 2 files changed, 1 insertion(+), 8 deletions(-) create mode 120000 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md index 356505e2..d4455119 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -413,14 +413,6 @@ update/ # 版本更新 - [贡献指南](CONTRIBUTING.md) - [行为准则](CODE_OF_CONDUCT.md) -## 项目统计 - -- **文档页面**:约 482 个 Markdown 文件 -- **代码示例**:约 235 个 Zig 源文件 -- **支持版本**:6 个(Zig 0.11 - 0.16) -- **构建配置**:6 个版本特定构建脚本 -- **CI 测试矩阵**:18 种组合(3 个操作系统 × 6 个版本) - --- 最后更新:2025-10-22(由 AI 助手自动生成) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 00000000..55bf822d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +./AGENTS.md \ No newline at end of file