diff --git a/.autocorrectrc b/.autocorrectrc index 1210625..cd4401c 100644 --- a/.autocorrectrc +++ b/.autocorrectrc @@ -1,8 +1,10 @@ -file_types: - smd: markdown - shtml: html - +# yaml-language-server: $schema=https://huacnlee.github.io/autocorrect/schema.json rules: space_word: 1 space_punctuation: 1 fullwidth: 1 +fileTypes: + smd: markdown + shtml: html +context: + codeblock: 1 diff --git a/content/contributing.smd b/content/contributing.smd index 9c3fb24..adabebf 100644 --- a/content/contributing.smd +++ b/content/contributing.smd @@ -14,7 +14,7 @@ Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文 ## [供稿方式]($heading.id('contribute')) 1. Fork 仓库 https://github.com/zigcc/zigcc.github.io -2. 在 `content/post` 内添加自己的文章(smd格式),文件命名为: `${YYYY}-${MM}-${DD}-${SLUG}.smd` +2. 在 `content/post` 内添加自己的文章(smd 格式),文件命名为: `${YYYY}-${MM}-${DD}-${SLUG}.smd` 3. 文件开始需要包含一些描述信息,例如: ```ziggy diff --git a/content/learn/coding-in-zig.smd b/content/learn/coding-in-zig.smd index 1ca7ea2..1001544 100644 --- a/content/learn/coding-in-zig.smd +++ b/content/learn/coding-in-zig.smd @@ -135,7 +135,7 @@ pub fn main() !void { try stdout.print("Please enter a name: ", .{}); if (try stdin.readUntilDelimiterOrEof(&buf, '\n')) |line| { var name = line; - // Windows平台换行以`\r\n`结束 + // Windows 平台换行以 `\r\n` 结束 // 所以需要截取\r以获取控制台输入字符 if (builtin.os.tag == .windows) { name = @constCast(std.mem.trimRight(u8, name, "\r")); @@ -222,7 +222,7 @@ defer { 现在你可以忘掉我们的 `IntList` 和我们创建的通用替代方案了。Zig 标准库中有一个动态数组实现:`std.ArrayList(T)`。 -它是相当标准的东西,但由于它如此普遍需要和使用的数据结构,值得看看它的实际应用: +它是相当标准的东西,但由于它如此普遍需要和使用的数据结构,值得看看它的实际应用: ```zig const std = @import("std"); @@ -459,7 +459,7 @@ zig build install -Doptimize=ReleaseSmall -Dtarget=x86_64-windows-gnu 除了默认的『安装』步骤外,可执行文件通常还会增加两个步骤:『运行』和『测试』。一个库可能只有一个『测试』步骤。对于基本的无参数即可运行的程序来说,只需要在构建文件的最后添加四行: ```zig -// 在这行代码后添加下面的代码: b.installArtifact(exe); +// 在这行代码后添加下面的代码:b.installArtifact(exe); const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); @@ -495,7 +495,7 @@ test "dummy build test" { 现在运行 `zig build test`时,应该会出现测试失败。如果你修复了测试,并再次运行 `zig build test`,你将不会得到任何输出。默认情况下,Zig 的测试运行程序只在失败时输出结果。如果你像我一样,无论成功还是失败,都想要一份总结,那就使用 `zig build test --summary all`。 -这是启动和运行构建系统所需的最低配置。但是请放心,如果你需要构建你的程序,Zig 内置的功能大概率能覆盖你的需求。最后,你可以(也应该)在你的项目根目录下使用 `zig init`,让 Zig 为你创建一个文档齐全的 `build.zig` 文件。 +这是启动和运行构建系统所需的最低配置。但是请放心,如果你需要构建你的程序,Zig 内置的功能大概率能覆盖你的需求。最后,你可以(也应该)在你的项目根目录下使用 `zig init`,让 Zig 为你创建一个文档齐全的 `build.zig` 文件。 ## [第三方依赖]($heading.id('third-party-dependencies')) @@ -655,12 +655,12 @@ _ = b.addModule("calc", .{ 要使用这一依赖关系,我们需要对 `build.zig` 进行一处修改: ```zig -// 将这些代码: +// 将这些代码: const calc_module = b.addModule("calc", .{ .root_source_file = b.path("calc/calc.zig"), }); -// 替换成: +// 替换成: const calc_dep = b.dependency("calc", .{.target = target,.optimize = optimize}); const calc_module = calc_dep.module("calc"); ``` diff --git a/content/learn/heap-memory.smd b/content/learn/heap-memory.smd index 1e2b166..69513df 100644 --- a/content/learn/heap-memory.smd +++ b/content/learn/heap-memory.smd @@ -143,7 +143,7 @@ fn allocLower(allocator: Allocator, str: []const u8) ![]const u8 { } ``` -上面的代码没问题。但以下用法不是: +上面的代码没问题。但以下用法不是: ```zig // 对于这个特定的代码,我们应该使用 std.ascii.eqlIgnoreCase @@ -290,7 +290,7 @@ pub fn main() !void { const T = std.heap.GeneralPurposeAllocator(.{}); var gpa = T{}; -// 等同于: +// 等同于: var gpa = std.heap.GeneralPurposeAllocator(.{}){}; ``` @@ -421,7 +421,7 @@ var larger = try self.allocator.alloc(i64, len * 2); self.allocator.free(self.items); ``` -将`items`复制到我们的 `larger` 切片中后, 添加最后一行`free`可以解决泄漏的问题。如果运行 `zig test learning.zig`,便不会再有错误。 +将`items`复制到我们的 `larger` 切片中后,添加最后一行`free`可以解决泄漏的问题。如果运行 `zig test learning.zig`,便不会再有错误。 ## [ArenaAllocator]($heading.id('arena-allocator')) @@ -504,7 +504,7 @@ defer list.deinit(); ... ``` -由于 `IntList` 接受的参数是 `std.mem.Allocator`, 因此我们不需要做什么改变。如果 `IntList`内部创建了自己的 `ArenaAllocator`,那也是可行的。允许在`ArenaAllocator`内部创建`ArenaAllocator`。 +由于 `IntList` 接受的参数是 `std.mem.Allocator`,因此我们不需要做什么改变。如果 `IntList`内部创建了自己的 `ArenaAllocator`,那也是可行的。允许在`ArenaAllocator`内部创建`ArenaAllocator`。 最后举个简单的例子,我上面提到的 HTTP 服务器在响应中暴露了一个 `ArenaAllocator`。一旦发送了响应,它就会被清空。由于`ArenaAllocator`的生命周期可以预测(从请求开始到请求结束),因此它是一种高效的选择。就性能和易用性而言,它都是高效的。 diff --git a/content/learn/language-overview-1.smd b/content/learn/language-overview-1.smd index f6c4e1e..dcf812f 100644 --- a/content/learn/language-overview-1.smd +++ b/content/learn/language-overview-1.smd @@ -39,11 +39,11 @@ pub const User = struct { ## [模块引用]($heading.id('module-reference')) -很少有程序是在没有标准库或外部库的情况下以单个文件编写的。我们的第一个程序也不例外,它使用 Zig 的标准库来进行打印输出。 Zig 的模块系统非常简单,只依赖于 `@import` 函数和 `pub` 关键字(使代码可以在当前文件外部访问)。 +很少有程序是在没有标准库或外部库的情况下以单个文件编写的。我们的第一个程序也不例外,它使用 Zig 的标准库来进行打印输出。Zig 的模块系统非常简单,只依赖于 `@import` 函数和 `pub` 关键字(使代码可以在当前文件外部访问)。 > 以 `@` 开头的函数是内置函数。它们是由编译器提供的,而不是标准库提供的。 -我们通过指定模块名称来引用它。 Zig 的标准库以 `std` 作为模块名。要引用特定文件,需要使用相对路径。例如,将 `User` 结构移动到它自己的文件中,比如 `models/user.zig`: +我们通过指定模块名称来引用它。Zig 的标准库以 `std` 作为模块名。要引用特定文件,需要使用相对路径。例如,将 `User` 结构移动到它自己的文件中,比如 `models/user.zig`: ```zig // models/user.zig @@ -295,7 +295,7 @@ const a = [_]i32{1, 2, 3, 4, 5}; const b = a[1..4]; ``` -在上述代码中, `b` 是一个长度为 3 的切片,并且是一个指向 `a` 的指针。但是因为我们使用编译时已知的值来对数组进行切片(即 `1` 和 `4`)所以长度 `3` 在编译时也是已知。 Zig 编译器能够分析出来这些信息,因此 `b` 不是一个切片,而是一个指向长度为 3 的整数数组的指针。具体来说,它的类型是 `*const [3]i32`。所以这个切片的示例被 Zig 编译器的强大推导能力挫败了。 +在上述代码中, `b` 是一个长度为 3 的切片,并且是一个指向 `a` 的指针。但是因为我们使用编译时已知的值来对数组进行切片(即 `1` 和 `4`)所以长度 `3` 在编译时也是已知。Zig 编译器能够分析出来这些信息,因此 `b` 不是一个切片,而是一个指向长度为 3 的整数数组的指针。具体来说,它的类型是 `*const [3]i32`。所以这个切片的示例被 Zig 编译器的强大推导能力挫败了。 在实际代码中,切片的使用可能会多于数组。无论好坏,程序的运行时信息往往多于编译时信息。不过,在下面这个例子中,我们必须欺骗 Zig 编译器才能得到我们想要的示例: diff --git a/content/learn/pointers.smd b/content/learn/pointers.smd index e061155..137de17 100644 --- a/content/learn/pointers.smd +++ b/content/learn/pointers.smd @@ -130,7 +130,7 @@ pub fn main() void { } ``` -我们最初的目标是通过`levelUp`函数将用户的`power`值增加 1 。我们已经让代码编译通过,但当我们打印`power`时,它仍然是原始值。虽然有点跳跃,但让我们修改代码,在 `main` 和 `levelUp` 中打印 `user`的地址: +我们最初的目标是通过`levelUp`函数将用户的`power`值增加 1。我们已经让代码编译通过,但当我们打印`power`时,它仍然是原始值。虽然有点跳跃,但让我们修改代码,在 `main` 和 `levelUp` 中打印 `user`的地址: ```zig pub fn main() void { diff --git a/content/learn/stack-memory.smd b/content/learn/stack-memory.smd index 7637369..f144473 100644 --- a/content/learn/stack-memory.smd +++ b/content/learn/stack-memory.smd @@ -57,7 +57,7 @@ levelUp: user -> ------------- (id: 1043368ec) | ------------- ``` -`levelUp` 紧接在 `main` 之后是有原因的:这是我们的简化版调用栈。当我们的程序启动时,`main` 及其局部变量被推入调用栈。当 `levelUp` 被调用时,它的参数和任何局部变量都会被添加到调用栈上。重要的是,当 `levelUp` 返回时,它会从栈中弹出。 在 `levelUp` 返回并且控制权回到 `main` 后,我们的调用栈如下所示: +`levelUp` 紧接在 `main` 之后是有原因的:这是我们的简化版调用栈。当我们的程序启动时,`main` 及其局部变量被推入调用栈。当 `levelUp` 被调用时,它的参数和任何局部变量都会被添加到调用栈上。重要的是,当 `levelUp` 返回时,它会从栈中弹出。在 `levelUp` 返回并且控制权回到 `main` 后,我们的调用栈如下所示: ``` main: user -> ------------- (id: 1043368d0) @@ -128,7 +128,7 @@ User 1 has power of 10 User 2 has power of 20 ``` -但实际上: +但实际上: ```bash User 2 has power of 20 diff --git a/content/learn/style-guide.smd b/content/learn/style-guide.smd index 50c0579..cc9d640 100644 --- a/content/learn/style-guide.smd +++ b/content/learn/style-guide.smd @@ -8,7 +8,7 @@ > 原文地址: -本小节的主要内容是介绍 Zig 编译器强制遵守的 2 条规则,以及 Zig 标准库的命名惯例(naming convention)。 +本小节的主要内容是介绍 Zig 编译器强制遵守的 2 条规则,以及 Zig 标准库的命名惯例 (naming convention)。 ## [未使用变量 Unused Variable]($heading.id('unused-variable')) @@ -27,7 +27,7 @@ fn add(a: i64, b: i64) i64 { } ``` -第一个编译错误,源自于`sum`是一个未使用的本地常量。第二个编译错误,在于在函数`add`的所有形参中,`b`是一个未使用的函数参数。对于这段代码来说,它们是比较明显的漏洞。但是在实际编程中,代码中包含未使用变量和函数形参并非完全不合理。在这种情况下,我们可以通过将未使用变量赋值给`_`(下划线)的方法,避免编译器报错: +第一个编译错误,源自于`sum`是一个未使用的本地常量。第二个编译错误,在于在函数`add`的所有形参中,`b`是一个未使用的函数参数。对于这段代码来说,它们是比较明显的漏洞。但是在实际编程中,代码中包含未使用变量和函数形参并非完全不合理。在这种情况下,我们可以通过将未使用变量赋值给`_`(下划线)的方法,避免编译器报错: ```zig const std = @import("std"); diff --git a/content/monthly/202209.smd b/content/monthly/202209.smd index 8290d3f..d83e08a 100644 --- a/content/monthly/202209.smd +++ b/content/monthly/202209.smd @@ -11,10 +11,10 @@ 在 9/10 号左右,在 Twitter 上牵起了一小波关于 Zig VS Rust 的小火花,以至于最后 Zig 创始人 Andrew Kelley [发推](https://twitter.com/andy_kelley/status/1568679389113757698)表示 -Let us exist。这里稍微整理下这件事情的过程: 本次事件主要涉及两个人: +Let us exist。这里稍微整理下这件事情的过程:本次事件主要涉及两个人: -- Rust 核心贡献者: Patrick Walton -- Zig 社区 VP: Loris Cro +- Rust 核心贡献者:Patrick Walton +- Zig 社区 VP:Loris Cro ## [时间线]($heading.id('timeline')) diff --git a/content/monthly/202308.smd b/content/monthly/202308.smd index fde2fab..af0b175 100644 --- a/content/monthly/202308.smd +++ b/content/monthly/202308.smd @@ -210,7 +210,7 @@ Andrew 关于构建系统的视频,[B [Wrap your NIF with Zig](https://rbino.com/posts/wrap-your-nif-with-zig/) NIF 是 Elixir 中进行 FFI 调用的方式,如果用原生 C -接口来用,会需要写很多胶水代码, 作者这里用 comptime 特性来定义了一个 +接口来用,会需要写很多胶水代码,作者这里用 comptime 特性来定义了一个 `make_nif_wrapper` 来简化 NIF 的实现,这个技巧在与 C 项目交互时十分有用。 diff --git a/content/monthly/202403.smd b/content/monthly/202403.smd index 5334585..fff4b71 100644 --- a/content/monthly/202403.smd +++ b/content/monthly/202403.smd @@ -50,7 +50,7 @@ Autodoc,之前的实现问题很多。比如: wasm 就可以调用 - 功能更强,因为新设计方案不再处理 - ZIR,而是直接处理源文件,这意味着它拥有100% + ZIR,而是直接处理源文件,这意味着它拥有 100% 的信息,不需要向后拼凑任何东西。 - sources.tar 文件经 HTTP 层解压后,直接进入 wasm 模块的内存。使用 @@ -151,7 +151,7 @@ errdefer comptime unreachable ``` 文中称这个是 Zig 的巅峰用法😅, `errdefer unreachable` -还比较好理解,即在执行出错时,执行 unreachable ,加上 comptime 呢? +还比较好理解,即在执行出错时,执行 unreachable,加上 comptime 呢? 其实这是阻止 Zig 编译器生产错误处理的代码,即在编译时期保证下面的逻辑不会出错,确实用的很巧妙!一个简单的例子: @@ -214,7 +214,7 @@ test.zig:4:23: error: reached unreachable code +} ``` -可以看到, 这么修改后,就可以保证 `map.deinit(allocator)` +可以看到,这么修改后,就可以保证 `map.deinit(allocator)` 语句之前没有错误可能产生!读者可以细细品味一下这个用法。 另一个小技巧是 errdefer 竟然支持错误捕获,即下面这种用法: diff --git a/content/monthly/202404.smd b/content/monthly/202404.smd index 14c741a..6820aa4 100644 --- a/content/monthly/202404.smd +++ b/content/monthly/202404.smd @@ -29,7 +29,7 @@ Zig 生态的发展是十分关键的一步,试想一个项目用到的依赖 这个版本解决了,用户可以基于 Step 构成的有向无环图来编译自己的项目,不需要再折腾 CMake、Makefile、Vcpkg、Git submodule 等工具,所有的依赖使用 zon -来管理即可。 读者如果对 Zig 构建系统还不熟悉,可以参考: +来管理即可。读者如果对 Zig 构建系统还不熟悉,可以参考: - 官方文档:[Zig Build System](https://ziglang.org/learn/build-system/) - Zig 升级: diff --git a/content/monthly/202405.smd b/content/monthly/202405.smd index ac5d4f3..98634b4 100644 --- a/content/monthly/202405.smd +++ b/content/monthly/202405.smd @@ -31,7 +31,7 @@ Zig,这是个不错的经验参考。 - 十分简洁,import 返回的是一个 struct,和其他变量一样使用 - 与 C 无缝交换, - 具有 Result 效果的错误处理 -- 唯一缺失的就是『接口』,但这一点并不是很关键,就像在 C里也没有,但是 C +- 唯一缺失的就是『接口』,但这一点并不是很关键,就像在 C 里也没有,但是 C 可以做任何事 ### [Zig's New CLI Progress Bar Explained]($heading.id('zigs-new-cli-progress-bar-explained')) diff --git a/content/monthly/202406.smd b/content/monthly/202406.smd index d79eb53..21f1cf1 100644 --- a/content/monthly/202406.smd +++ b/content/monthly/202406.smd @@ -9,7 +9,7 @@ ## [重大事件]($heading.id('major-events')) 2024-06-07,0.13.0 发布,历时不足 2 个月,有 73 位贡献者,一共进行了 415 -次提交! 这是一个相对较短的发布周期,主要原因是工具链升级,例如升级到 +次提交!这是一个相对较短的发布周期,主要原因是工具链升级,例如升级到 [LLVM 18](https://ziglang.org/download/0.13.0/release-notes.html#LLVM-18)。 @@ -36,7 +36,7 @@ const map = std.StaticStringMap(T).initComptime(kvs_list); [原文链接](https://www.openmymind.net/Leveraging-Zigs-Allocators/) 老朋友 openmymind 的又一篇好文章:如何利用 Zig 的 Allocator -来实现请求级别的内存分配。 Zig Allocator +来实现请求级别的内存分配。Zig Allocator 的最佳应用。[这里](/post/2024-06-16-leveraging-zig-allocator/)它的中文翻译。 ``` zig @@ -107,7 +107,7 @@ fn run(worker: *Worker) void { [原文链接](https://ludwigabap.bearblog.dev/zig-vs-rust-at-work-the-choice-we-made/) 这篇文章作者描述了所在公司在改造老 C/C++ 项目时,为什么选择了 Zig 而不是 -Rust。 重写的项目运行在多个平台上(Web、移动端、VR +Rust。重写的项目运行在多个平台上(Web、移动端、VR 设备),因此最靠谱的方案就是暴露一个 C API,然后通过 FFI 来调用。在做决策时,重点关注以下两点: diff --git a/content/monthly/202407.smd b/content/monthly/202407.smd index d52f33c..4101d57 100644 --- a/content/monthly/202407.smd +++ b/content/monthly/202407.smd @@ -12,12 +12,12 @@ Stackoverflow 2024 年的[调查报告](https://survey.stackoverflow.co/2024/technology),指出 Zig 语言是最热门的编程语言之一,并且 Zig -开发者的薪水都很高,平均年收入为75,332美元! +开发者的薪水都很高,平均年收入为 75,332 美元! [Zig 受欢迎程度]($image.siteAsset('images/stackoverflow2024-salary.webp')) [Zig 薪水对比]($image.siteAsset('images/stackoverflow2024-salary.webp')) - 尽管使用 Zig 语言的开发者仅占调查人数的1%,但上升趋势明显。Zig 语言的倡导者、自由和开放源码软件开发者 Ali Cheragi 说: + 尽管使用 Zig 语言的开发者仅占调查人数的 1%,但上升趋势明显。Zig 语言的倡导者、自由和开放源码软件开发者 Ali Cheragi 说: > Zig > 的魅力在于它的简洁性、现代设计以及在底层控制和运行时安全性之间取得的平衡。 @@ -29,9 +29,9 @@ Zig 开发者的一些观点: - 与许多其他语言不同,Zig 可以与现有的 C 代码实现真正的无缝互操作。出于多种原因,这一点至关重要。 - Zig 正在对大量编程基础架构进行彻底改造,而这些基础架构在过去 40 - 年里无人敢碰。 C 和 C++ + 年里无人敢碰。C 和 C++ 是著名的核心编程语言,在这两种语言中,你可以完全控制硬件。 - 但与此同时,这些语言的工具链却非常糟糕。 Zig + 但与此同时,这些语言的工具链却非常糟糕。Zig 允许用户涉猎这些核心编程语言,但可以使用更好的工具链,兼容各种语言和更丰富的功能。 ## [观点/教程]($heading.id('opinion-tutorial')) @@ -41,7 +41,7 @@ Zig 开发者的一些观点: [原文链接](https://kristoff.it/blog/improving-your-zls-experience/) Loris Cro 的最新文章,介绍了一个改进 Zig -编码体验的小技巧,十分推荐大家使用。具体来说是这样的: 通过配置 +编码体验的小技巧,十分推荐大家使用。具体来说是这样的:通过配置 zls,达到保存文件时,自动进行源码检查,而且速度非常快! ``` json @@ -52,7 +52,7 @@ zls,达到保存文件时,自动进行源码检查,而且速度非常快 ``` 将上述内存保存到 zls 的配置文件中,(路径可以通过 -`zls --show-config-path` 查看 ),zls 就会在保存时,自动执行 +`zls --show-config-path` 查看),zls 就会在保存时,自动执行 `zig build check` ,这个 `check` 一般来说是这样的: ``` zig @@ -70,7 +70,7 @@ check.dependOn(&exe_check.step); 由于 Zig 目前的一个 bug([\#18877](https://github.com/ziglang/zig/issues/18877)),这个 `exe_check` 不能作为 install、run 的依赖,否则在编译时,就不会增加 -`-fno-emit-bin` 选项。 而这个选项的作用就是让 Zig +`-fno-emit-bin` 选项。而这个选项的作用就是让 Zig 来分析我们的代码,但是不会调用 LLVM 来生成最终的二进制文件,因此速度会比较快。 @@ -153,7 +153,7 @@ fn get_window_messages() [65536][:0]const u8 { 帮助程序,这些帮助程序可以很快地读取大多数问题的输入。 - Zig 支持所有大小的 int,一直到 u65536。如果出现溢出,请尝试使用更大的整数类型。我在一些问题上使用了 - u128和 i128。 + u128 和 i128。 - StringToEnum 是解析受限制的字符串或字符集的一个简单技巧。 - 可以在结构上定义一个 format 方法,使它们按照您的喜好打印。 - 尽量避免将字符串复制到 StringHashMap 中用作键。从 JS @@ -168,9 +168,9 @@ fn get_window_messages() [65536][:0]const u8 { - 用于处理字符串的许多函数都在 std.mem 中,例如 std.mem.eql 和 std.mem.startsWith - 使用 std.met.eql 比较 structs,而不是 `=` -- 有一个按偏移量和长度切片的技巧: `array [start..][0..length]` +- 有一个按偏移量和长度切片的技巧:`array [start..][0..length]` - 记忆函数通常是很有用的。我不知道 Zig 有没有通用的方法 -- 调试构建比优化构建慢得多,有时候慢10倍。如果你在一个合理的时间内得到一个答案的10倍之内,尝试一个不同的发布模式。 +- 调试构建比优化构建慢得多,有时候慢 10 倍。如果你在一个合理的时间内得到一个答案的 10 倍之内,尝试一个不同的发布模式。 - 迭代时不要对数组列表进行修改 - 在 JavaScript 允许您内联表达式的某些情况下,您可能需要分解出一个变量来澄清生存期。看看[这个问题](https://github.com/ziglang/zig/issues/12414)。 diff --git a/content/monthly/202410.smd b/content/monthly/202410.smd index a2bf63d..a22123a 100644 --- a/content/monthly/202410.smd +++ b/content/monthly/202410.smd @@ -13,15 +13,15 @@ Mitchell 在其最新的博客上宣布:我和我的妻子向 Zig 软件基金会 (ZSF) 捐赠了 300,000 美元。 -> 两年内每年分期支付15万美元。第一期已经转账。 +> 两年内每年分期支付 15 万美元。第一期已经转账。 -我从 2019 年的某个时候开始关注 Zig 项目。 我在 2021 -年公开分享了我对该项目的兴奋之情。 同年晚些时候,我开始使用 Zig,到 2022 -年初,我开始撰写关于 Zig 的文章,并为编译器做出贡献。 2023 +我从 2019 年的某个时候开始关注 Zig 项目。我在 2021 +年公开分享了我对该项目的兴奋之情。同年晚些时候,我开始使用 Zig,到 2022 +年初,我开始撰写关于 Zig 的文章,并为编译器做出贡献。2023 年,我公开分享了用 Zig 编写的终端项目 Ghostty。 如今,我大部分的编码时间都花在了 Zig 上。 -我的家人喜欢支持我们相信的事业2。 +我的家人喜欢支持我们相信的事业 2。 作为其中的一部分,我们希望支持那些我们认为可以带来变革和影响的独立软件项目,这既是回馈给我如此之多的社区的一种方式,更重要的是,这也是彰显和鼓励为热爱而构建的文化的一种方式。 Zig 就是这样一个项目。 diff --git a/content/monthly/202411.smd b/content/monthly/202411.smd index dc4eace..7577d89 100644 --- a/content/monthly/202411.smd +++ b/content/monthly/202411.smd @@ -23,7 +23,7 @@ 里就有四次!),而且都是指针的树结构非常不利用重复利用 CPU,Carbon 编译器就有一种更紧凑的 AST 表示。 -- Compile time AST query processing。Lint +- Compile time AST query processing. Lint 的规则大部分都是模式匹配,大部分时候都有多个嵌套的 if 逻辑,为了简化插件开发者,eslint 采用了一种 [esquery](https://estools.github.io/esquery/) 的语法,示例: @@ -92,7 +92,7 @@ 更重要的,作者最后提到 AoC 可能不是学习 Zig 的最好方式: -> 虽然 AoC 非常有趣,但它并不是练习软件工程的方法。 每个 AoC +> 虽然 AoC 非常有趣,但它并不是练习软件工程的方法。每个 AoC > 练习都要求你找到一个问题的解决方案,虽然你需要编写一个程序来解决这个问题, > 但你的程序将是一个只需运行一次(一次正确)的一次性脚本。 > @@ -126,7 +126,7 @@ Zig,利用 LLVM 后端,以 wasm32-wasi 为目标生成的二进制文件。 cc 编译 zig1.c 就可以得到 stage1 的 zig 编译器 - 之后再用 zig1 编译 zig 源码,由于 zig1 之后 C 后端,因此这里得到的产物是 zig2.c,再利用 cc 就可以可以 stage2 的 - zig。 zig2 功能上已经完备,但是速度很慢(没有经过 LLVM 优化) + zig。zig2 功能上已经完备,但是速度很慢(没有经过 LLVM 优化) - 最后再用 zig2 继续编译 zig 源码,得到最后的 zig3,这也是我们下载 zig 安装包时包含的版本 - 如果再继续用 zig3 来编译 zig 源码,得到的 zig4 会和 zig3 一模一样。 diff --git a/content/post/0.14.smd b/content/post/0.14.smd index e262e25..b2d9e1e 100644 --- a/content/post/0.14.smd +++ b/content/post/0.14.smd @@ -85,7 +85,7 @@ Zig 0.14.0 版本是经过 **9 个月的工作**,由 **251 位不同的贡献 - **基于现有模块创建 Artifacts (Creating Artifacts from Existing Modules):** 修改了构建系统 API,允许从现有的 std.Build.Module 对象创建 Compile 步骤,使模块图的定义更清晰,组件复用更容易。旧的 API 用法已被弃用。 - 引用:Zig 0.14.0 modifies the build system APIs for creating Compile steps, allowing them to be created from existing std.Build.Module objects. - **允许包按名称暴露任意 LazyPath (Allow Packages to Expose Arbitrary LazyPaths by Name):** 新增 std.Build.addNamedLazyPath 和 std.Build.Module.namedLazyPath 方法,允许依赖包按名称暴露生成的 LazyPath 给其依赖者使用。 -- **addLibrary 函数:** 新增 addLibrary 函数,取代 addSharedLibrary 和 addStaticLibrary,使得在 build.zig 中更容易切换链接模式,并与 linkLibrary 名称更匹配。 +- **addLibrary 函数:** 新增 addLibrary 函数,取代 addSharedLibrary 和 addStaticLibrary,使得在 build.zig 中更容易切换链接模式,并与 linkLibrary 名称更匹配。 - **文件系统监控 (File System Watching):** #22105 - **新包哈希格式 (New Package Hash Format):** 引入新的包哈希格式,包含 32 位 id 和 32 位校验和,用于在去中心化生态系统中唯一标识包。当 fork 项目时,如果上游仍维护,应该重新生成 fingerprint。 - **WriteFile Step, RemoveDir Step, Fmt Step:** 新增或改进了这些构建步骤。 diff --git a/content/post/2023-09-05-bog-gc-1.smd b/content/post/2023-09-05-bog-gc-1.smd index 59379cd..a7edad6 100644 --- a/content/post/2023-09-05-bog-gc-1.smd +++ b/content/post/2023-09-05-bog-gc-1.smd @@ -18,17 +18,17 @@ GC 是一种垃圾回收的机制,主要是针对`heap`区域的内存管理策略。在堆中的内存分配是按照指数级增长的大小进行的,此外还有一个专门用于非常大对象的特殊子堆。这种方法的一个优点可能是它可以高效地处理各种大小的内存请求。 -以公式来进行表示: +以公式来进行表示: $$ Heap = (M, S, (H_3, H_4, ..., H_{12})) $$ -其中: +其中: 1. M 是特殊的区域,用于存储大对象 2. S 是空闲区域 -3. H 是子堆,用于存储小对象,$H_i$表示大小为$2^i$的子堆,每次新增都为之前的两倍 +3. H 是子堆,用于存储小对象,$H_i$表示大小为$2^i$的子堆,每次新增都为之前的两倍 ```=html
@@ -89,16 +89,16 @@ graph TD
 
 在这种移动 GC 中,有"分代复制收集器"和"Cheney 复制收集器"。
 
-分代复制收集器(Generational Copying Collector): 是垃圾收集的一种常见方法,特别是在函数式编程语言中。它假设新创建的对象很快就会变得不可达(即"死亡"),而老的对象则更可能持续存在。因此,内存被分成两个或更多的"代",新对象在"新生代"中创建,当它们存活足够长的时间时,它们会被移到"老生代"。
+分代复制收集器 (Generational Copying Collector): 是垃圾收集的一种常见方法,特别是在函数式编程语言中。它假设新创建的对象很快就会变得不可达(即"死亡"),而老的对象则更可能持续存在。因此,内存被分成两个或更多的"代",新对象在"新生代"中创建,当它们存活足够长的时间时,它们会被移到"老生代"。
 
-Cheney 的拷贝收集器: 是一种用于半区(semi-space)的拷贝垃圾收集器。它的工作原理是将可用内存分为两半,并且只在其中一半中分配对象。当这一半用完时,收集器通过拷贝活跃对象到另一半空间来进行垃圾收集。然后,原先的那一半空间就完全清空,成为新的可用空间。Cheney 的收集器特别适用于处理短生命周期的数据,因为它可以快速地只拷贝活跃的数据,而忽略死亡的数据。这使得它在处理大量短生命周期数据的程序(例如函数式程序)时,对于其"次要收集"(minor collection,即仅仅回收新生代数据的收集)非常高效。**这种方法的优势在于它可以有效地处理内存碎片,因为通过复制活动对象到新位置,内存会被连续地占用。**
+Cheney 的拷贝收集器:是一种用于半区(semi-space)的拷贝垃圾收集器。它的工作原理是将可用内存分为两半,并且只在其中一半中分配对象。当这一半用完时,收集器通过拷贝活跃对象到另一半空间来进行垃圾收集。然后,原先的那一半空间就完全清空,成为新的可用空间。Cheney 的收集器特别适用于处理短生命周期的数据,因为它可以快速地只拷贝活跃的数据,而忽略死亡的数据。这使得它在处理大量短生命周期数据的程序(例如函数式程序)时,对于其"次要收集"(minor collection,即仅仅回收新生代数据的收集)非常高效。**这种方法的优势在于它可以有效地处理内存碎片,因为通过复制活动对象到新位置,内存会被连续地占用。**
 
-特点:
+特点:
 
 - **任何精确的 copy gc 都需要 runtime 系统定位和更新每个堆分配数据的所有指针**
 - 在传统的垃圾收集策略(移动 GC)中,压缩是一种常用的技术,它会将活跃的对象移到内存的一个连续区域中,从而释放出未使用的内存。换言之,就是整理内存碎片。
 
-在非移动 GC 中,有"标记-清除"。
+在非移动 GC 中,有"标记 - 清除"。
 
 **不需要进行压缩和对象移动这一特性是非常重要**,指针的值(即对象的内存地址)是固定的,也不需要花时间更新移动的地址。这使得非移动 GC 非常适合于需要与其他语言进行交互的语言,因为它们可以在不需要额外的工作的情况下访问内存中的对象。而且不需要移动对象的这一特性对于支持多原生线程也是有益的。在多线程环境中,如果对象在内存中的位置不断移动,那么线程之间的协调和同步将变得更加复杂。因此,避免对象移动可以简化多线程编程。
 
@@ -139,7 +139,7 @@ Cheney 的拷贝收集器: 是一种用于半区(semi-space)的拷贝垃圾
 
 ### [Bitmap]($heading.id('bitmap'))
 
-由于 Bog 的 GC 本质上还是采用了"标记-清除",所以利用位图来记录数据是必不可少的。在 Bog 中,我们采用了"位图记录数据"的方式来进行 GC。而为了提高效率,我们增加了元位图的概念,即每 4 个元素对应一个元位图,用于记录多空间的占用状态,并且根据 heap 的对象时间增加深度。
+由于 Bog 的 GC 本质上还是采用了"标记 - 清除",所以利用位图来记录数据是必不可少的。在 Bog 中,我们采用了"位图记录数据"的方式来进行 GC。而为了提高效率,我们增加了元位图的概念,即每 4 个元素对应一个元位图,用于记录多空间的占用状态,并且根据 heap 的对象时间增加深度。
 
 ### [Implementation]($heading.id('implementation'))
 
@@ -184,5 +184,5 @@ const Page = struct {
 3. `meta`: 表示一个 Page 中每个 Value 对象的状态。这里我们使用了一个枚举类型来表示状态,因为状态只有 4 种,所以可以用 2 位来表示。因此,我们可以使用一个 u32 来表示一个 Page 中所有 Value 对象的状态。这样,我们就可以使用一个 u32 来表示一个 Page 中所有 Value 对象的状态。每一个 State 可能对应一个在 values 字段中的 Value 对象的状态。
 4. `__padding` 的字段,用于填充额外的内存空间。它的大小由之前提到的 pad_size 决定,且是一个字节(u8)数组。这通常用于确保数据结构的内存对齐。
 5. `free`: 表示空闲或可用的空间数量、索引或其他与内存管理相关的信息
-6. `marked`: 表示已标记的空间数量、索引或其他与内存管理相关的信息,在垃圾收集的过程中用于检测是否应继续在此页面中检查值
+6. `marked`: 表示已标记的空间数量、索引或其他与内存管理相关的信息,在垃圾收集的过程中用于检测是否应继续在此页面中检查值
 7. `values`: 表示一个 Page 中的 Value 对象。它是一个 Value 对象的数组,其大小由 val_count 决定。
diff --git a/content/post/2023-09-05-hello-world.smd b/content/post/2023-09-05-hello-world.smd
index c711156..a280ab9 100644
--- a/content/post/2023-09-05-hello-world.smd
+++ b/content/post/2023-09-05-hello-world.smd
@@ -14,7 +14,7 @@
 ## [供稿方式]($heading.id('contribute'))
 
 1. Fork 仓库 https://github.com/zigcc/zigcc.github.io
-2. 在 `content/post` 内添加自己的文章(smd格式),文件命名为: `${YYYY}-${MM}-${DD}-${SLUG}.smd`
+2. 在 `content/post` 内添加自己的文章(smd 格式),文件命名为: `${YYYY}-${MM}-${DD}-${SLUG}.smd`
 3. 文件开始需要包含一些描述信息,例如:
 
 ```ziggy
@@ -33,4 +33,4 @@
 
 ## [本地预览]($heading.id('local-preview'))
 
-在写完文章后,可以使用 [zine](https://zine-ssg.io/) 进行本地预览,只需在项目根目录执行 `zine`,这会启动一个 HTTP 服务,默认的访问地址是: http://localhost:1990/
+在写完文章后,可以使用 [zine](https://zine-ssg.io/) 进行本地预览,只需在项目根目录执行 `zine`,这会启动一个 HTTP 服务,默认的访问地址是:http://localhost:1990/
diff --git a/content/post/2023-09-21-zig-midi.smd b/content/post/2023-09-21-zig-midi.smd
index ae50ae4..0efc81d 100644
--- a/content/post/2023-09-21-zig-midi.smd
+++ b/content/post/2023-09-21-zig-midi.smd
@@ -1,5 +1,5 @@
 ---
-.title = "Zig音频之MIDI —— 源码解读",
+.title = "Zig 音频之 MIDI —— 源码解读",
 .date = @date("2023-09-21T23:15:02+0800"),
 .author = "文轩",
 .layout = "post.shtml",
@@ -282,7 +282,7 @@ pub const Message = struct {
 
 ## [decode.zig]($heading.id('decodezig'))
 
-本文件是对 MIDI 文件的解码器, 提供了一组工具,可以从不同的输入源解析 MIDI 文件的各个部分。这样可以方便地读取和处理 MIDI 文件。
+本文件是对 MIDI 文件的解码器,提供了一组工具,可以从不同的输入源解析 MIDI 文件的各个部分。这样可以方便地读取和处理 MIDI 文件。
 
 ```zig
 const midi = @import("../midi.zig");
@@ -658,7 +658,7 @@ pub fn file(writer: anytype, f: midi.File) !void {
 }
 ```
 
-- message 函数:这是将 MIDI 消息编码为字节序列的函数, 将单个 MIDI 消息编码为其二进制形式。根据消息类型,这会向提供的 writer 写入一个或多个字节。若消息需要状态字节,并且不同于前一个消息的状态,函数会写入状态字节。接着,根据消息的种类,函数会写入所需的数据字节。
+- message 函数:这是将 MIDI 消息编码为字节序列的函数,将单个 MIDI 消息编码为其二进制形式。根据消息类型,这会向提供的 writer 写入一个或多个字节。若消息需要状态字节,并且不同于前一个消息的状态,函数会写入状态字节。接着,根据消息的种类,函数会写入所需的数据字节。
 
 - chunkToBytes 函数:将 MIDI 文件的块(Chunk)信息转换为 8 字节的二进制数据。这 8 字节中包括 4 字节的块类型和 4 字节的块长度。它复制块类型到前 4 个字节,然后写入块的长度到后 4 个字节,并返回结果。
 
@@ -666,7 +666,7 @@ pub fn file(writer: anytype, f: midi.File) !void {
 
 - int 函数:将一个整数编码为 MIDI 文件中的可变长度整数格式。在 MIDI 文件中,某些整数值使用一种特殊的编码格式,可以根据整数的大小变化长度。
 
-- metaEvent 函数:将 MIDI 元事件(Meta Event)编码为二进制数据, 这包括事件的类型和长度。具体则是编码一个元事件,首先写入其种类字节,然后是其长度。
+- metaEvent 函数:将 MIDI 元事件(Meta Event)编码为二进制数据,这包括事件的类型和长度。具体则是编码一个元事件,首先写入其种类字节,然后是其长度。
 
 - trackEvent 函数:编码轨道事件。轨道事件可以是元事件或 MIDI 事件,函数首先写入事件之间的时间差(delta 时间),然后根据事件类型(MetaEvent 或 MidiEvent)编码事件内容。
 
@@ -956,26 +956,26 @@ pub fn build(b: *Builder) void {
 }
 ```
 
-这个 build 比较复杂,我们逐行来解析:
+这个 build 比较复杂,我们逐行来解析:
 
 ```zig
 const test_all_step = b.step("test", "Run all tests in all modes.");
 ```
 
-- 使用 b.step()方法定义了一个名为 test 的步骤。描述是"在所有模式下运行所有测试"。
+- 使用 b.step() 方法定义了一个名为 test 的步骤。描述是"在所有模式下运行所有测试"。
 
 ```zig
 inline for (@typeInfo(std.builtin.Mode).Enum.fields) |field| {}
 ```
 
-- Zig 有几种构建模式,例如 Debug、ReleaseSafe 等, 上面则是为每种构建模式生成测试.
-  - 这里,@typeInfo()函数获取了一个类型的元信息。std.builtin.Mode 是 Zig 中定义的构建模式的枚举。Enum.fields 获取了这个枚举的所有字段。
+- Zig 有几种构建模式,例如 Debug、ReleaseSafe 等,上面则是为每种构建模式生成测试。
+  - 这里,@typeInfo() 函数获取了一个类型的元信息。std.builtin.Mode 是 Zig 中定义的构建模式的枚举。Enum.fields 获取了这个枚举的所有字段。
 
 ```zig
 const example_step = b.step("examples", "Build examples");
 ```
 
-- 配置示例构建,为所有示例创建的构建步骤.
+- 配置示例构建,为所有示例创建的构建步骤。
 
 ```zig
 const all_step = b.step("all", "Build everything and runs all tests");
diff --git a/content/post/2023-12-24-zig-build-explained-part1.smd b/content/post/2023-12-24-zig-build-explained-part1.smd
index c460f6e..021b654 100644
--- a/content/post/2023-12-24-zig-build-explained-part1.smd
+++ b/content/post/2023-12-24-zig-build-explained-part1.smd
@@ -6,7 +6,7 @@
 .draft = false,
 ---
 
-> - 原文链接: https://zig.news/xq/zig-build-explained-part-1-59lf
+> - 原文链接:https://zig.news/xq/zig-build-explained-part-1-59lf
 > - API 适配到 Zig 0.11.0 版本
 
 Zig 构建系统仍然缺少文档,对很多人来说,这是不使用它的致命理由。还有一些人经常寻找构建项目的秘诀,但也在与构建系统作斗争。
@@ -214,7 +214,7 @@ pub fn build(b: *std.build.Builder) void {
 3. 当我们调用 zig build install(或简称 zig build)时,它将创建 InstallArtifactStep。
 4. InstallArtifactStep 会将 exe 的输出文件注册到一个列表中,以便再次卸载它
 
-现在,当你调用 zig build 时,你会看到一个新的目录 zig-out 被创建了.看起来有点像这样:
+现在,当你调用 zig build 时,你会看到一个新的目录 zig-out 被创建了。看起来有点像这样:
 
 ```
 zig-out
@@ -293,7 +293,7 @@ RunStep 有几个函数可以为执行进程的 argv 添加值:
 info: All your codebase are belong to us.
 ```
 
-请注意,这里有一个重要的区别: 使用 RunStep 时,我们从 ./zig-cache/.../fresh 而不是 zig-out/bin/fresh 运行可执行文件!如果你加载的文件相对于可执行路径,这一点可能很重要。
+请注意,这里有一个重要的区别:使用 RunStep 时,我们从 ./zig-cache/.../fresh 而不是 zig-out/bin/fresh 运行可执行文件!如果你加载的文件相对于可执行路径,这一点可能很重要。
 
 RunStep 的配置非常灵活,可以通过 stdin 向进程传递数据,也可以通过 stdout 和 stderr 验证输出。你还可以更改工作目录或环境变量。
 
diff --git a/content/post/2023-12-28-zig-build-explained-part2.smd b/content/post/2023-12-28-zig-build-explained-part2.smd
index 923d74d..be70ab1 100644
--- a/content/post/2023-12-28-zig-build-explained-part2.smd
+++ b/content/post/2023-12-28-zig-build-explained-part2.smd
@@ -6,7 +6,7 @@
 .draft = false,
 ---
 
-> - 原文链接: https://zig.news/xq/zig-build-explained-part-2-1850
+> - 原文链接:https://zig.news/xq/zig-build-explained-part-2-1850
 > - API 适配到 Zig 0.11.0 版本
 
 ## [注释]($heading.id('annotation'))
@@ -78,7 +78,7 @@ pub fn build(b: *std.Build) void {
         .target = target,
         .optimize = optimize,
     });
-    // 这块调试了很久。API变了不会写,着了很久的文档和看了很久的代码
+    // 这块调试了很久。API 变了不会写,着了很久的文档和看了很久的代码
     exe.addCSourceFile(.{ .file = std.build.LazyPath.relative("main.c"), .flags = &.{} });
     exe.addCSourceFile(.{ .file = std.build.LazyPath.relative("buffer.c"), .flags = &.{} });
     //exe.linkLibC();
@@ -516,7 +516,7 @@ pub fn build(b: *std.Build) void {
 
 ## [混合使用 C 和 Zig 源代码]($heading.id('mixing-c-and-zig-source-code'))
 
-现在,是最后一章: 混合 C 代码和 Zig 代码!
+现在,是最后一章:混合 C 代码和 Zig 代码!
 
 为此,我们只需将 addExecutable 中的第二个参数设置为文件名,然后点击编译!
 
@@ -552,7 +552,7 @@ pub fn build(b: *std.Build) void {
 
 实际上,有一种情况现在还没有得到很好的支持:
 您应用程序的入口点现在必须在 Zig 代码中,因为根文件必须导出一个 pub fn main(...) ……。
-因此,如果你想将 C 项目中的代码移植到 Zig 中,你必须将 argc 和 argv 转发到你的 C 代码中,并将 C 代码中的 main 重命名为其他函数(例如 oldMain),然后在 Zig 中调用它。如果需要 argc 和 argv,可以通过 std.process.argsAlloc 获取。或者更好: 在 Zig 中重写你的入口点,然后从你的项目中移除一些 C 语言!
+因此,如果你想将 C 项目中的代码移植到 Zig 中,你必须将 argc 和 argv 转发到你的 C 代码中,并将 C 代码中的 main 重命名为其他函数(例如 oldMain),然后在 Zig 中调用它。如果需要 argc 和 argv,可以通过 std.process.argsAlloc 获取。或者更好:在 Zig 中重写你的入口点,然后从你的项目中移除一些 C 语言!
 
 ## [结论]($heading.id('conclusion'))
 
diff --git a/content/post/2023-12-29-zig-build-explained-part3.smd b/content/post/2023-12-29-zig-build-explained-part3.smd
index aceed8f..baca5d4 100644
--- a/content/post/2023-12-29-zig-build-explained-part3.smd
+++ b/content/post/2023-12-29-zig-build-explained-part3.smd
@@ -6,7 +6,7 @@
 .draft = false,
 ---
 
-> - 原文链接: https://zig.news/xq/zig-build-explained-part-3-1ima
+> - 原文链接:https://zig.news/xq/zig-build-explained-part-3-1ima
 > - API 适配到 Zig 0.11.0 版本
 
 从现在起,我将只提供一个最精简的 build.zig,用来说明解决一个问题所需的步骤。如果你想了解如何将所有这些文件粘合到一个构建文件中,请阅读本系列[第一篇文章]($link.page('post/2023-12-24-zig-build-explained-part1'))。
@@ -169,7 +169,7 @@ include
 
 ### [动态链接]($heading.id('dynamic-link'))
 
-要链接 libcurl,我们需要先添加 include 路径,然后向 zig 提供库的前缀和库名:(todo 代码有待验证,因为 curl 可能需要自己编译自己生成 static lib)
+要链接 libcurl,我们需要先添加 include 路径,然后向 zig 提供库的前缀和库名:(todo 代码有待验证,因为 curl 可能需要自己编译自己生成 static lib)
 
 ```zig
 //demo 3.3
@@ -220,7 +220,7 @@ pub fn build(b: *std.build.Builder) void {
 }
 ```
 
-对 addIncludeDir 的调用没有改变,但我们突然不再调用带 link 的函数了?你可能已经知道了: 静态库实际上就是对象文件的集合。在 Windows 上,这一点也很相似,据说 MSVC 也使用了相同的工具集。
+对 addIncludeDir 的调用没有改变,但我们突然不再调用带 link 的函数了?你可能已经知道了:静态库实际上就是对象文件的集合。在 Windows 上,这一点也很相似,据说 MSVC 也使用了相同的工具集。
 
 因此,静态库就像对象文件一样,通过 addObjectFile 传递给链接器,并由其解包。
 
@@ -324,7 +324,7 @@ pub fn build(b: *std.build.Builder) void {
 
 从这里可以看出,我们只是向 addSystemCommand 传递了一个选项数组,该数组将反映我们的命令行调用。然后,我们按照习惯创建可执行文件,并使用 dependOn 在 cmd 上添加步骤依赖关系。
 
-我们也可以反其道而行之,在编译程序时添加有关程序的小信息:
+我们也可以反其道而行之,在编译程序时添加有关程序的小信息:
 
 ```zig
 //demo3.6
diff --git a/content/post/2024-01-12-how-to-release-your-zig-applications.smd b/content/post/2024-01-12-how-to-release-your-zig-applications.smd
index 33d5271..77fec42 100644
--- a/content/post/2024-01-12-how-to-release-your-zig-applications.smd
+++ b/content/post/2024-01-12-how-to-release-your-zig-applications.smd
@@ -6,7 +6,7 @@
 .draft = false,
 ---
 
-> - 原文链接: https://zig.news/kristoff/how-to-release-your-zig-applications-2h90
+> - 原文链接:https://zig.news/kristoff/how-to-release-your-zig-applications-2h90
 > - API 适配到 Zig 0.12.0 版本
 > - 本文配套代码在[这里](https://github.com/zigcc/zigcc.github.io/tree/main/examples/20240112)找到
 
diff --git a/content/post/2024-04-06-zig-cpp.smd b/content/post/2024-04-06-zig-cpp.smd
index 3ea5e5a..b67110e 100644
--- a/content/post/2024-04-06-zig-cpp.smd
+++ b/content/post/2024-04-06-zig-cpp.smd
@@ -10,31 +10,31 @@
 
 ## ["元能力" vs "元类型"]($heading.id('meta-ability-vs-meta-type'))
 
-在我看来,C++的增强方式是希望赋予语言一种"元能力",能够让人重新发明新的类型,使得使用 C++的程序员使用自定义的类型,进行一种类似于"领域内语言"(DSL)编程。一个通常的说法就是 C++中任何类型定义都像是在模仿基本类型`int`。比如我们有一种类型 T,那么我们则需要定义 T 在以下几种使用场景的行为:
+在我看来,C++ 的增强方式是希望赋予语言一种"元能力",能够让人重新发明新的类型,使得使用 C++ 的程序员使用自定义的类型,进行一种类似于"领域内语言"(DSL)编程。一个通常的说法就是 C++ 中任何类型定义都像是在模仿基本类型`int`。比如我们有一种类型 T,那么我们则需要定义 T 在以下几种使用场景的行为:
 
 ```cpp
 T x; //构造
 T x = y; //隐式拷贝构造
 T x{y}; //显示拷贝构造
-x = y; //x的类型是T,复制运算符重载,当然也有可能是移动运算符重载。
+x = y; //x 的类型是 T,复制运算符重载,当然也有可能是移动运算符重载。
 //以及一大堆其他行为,比如析构等等。
 ```
 
-通过定义各种行为,程序员可以用 C++去模拟基础类型`int`,自定义的创造新类型。但是 Zig 却采取了另一条路,这里我觉得 Zig 的取舍挺有意思,即它剥夺了程序员定义新类型的能力,只遵循 C 的思路,即`struct`就是`struct`,他和`int`就是不一样的,没有必要通过各种运算符重载来制造一种"幻觉",模拟`int`。相反,Zig 吸收现代语言中最有用的"元类型",比如`slice`,`tuple`,`tagged union`等作为语言内置的基本类型,从这一点上对 C 进行增强。虽然这样降低了语言的表现力,但是却简化了设计,降低了"心智负担"。
+通过定义各种行为,程序员可以用 C++ 去模拟基础类型`int`,自定义的创造新类型。但是 Zig 却采取了另一条路,这里我觉得 Zig 的取舍挺有意思,即它剥夺了程序员定义新类型的能力,只遵循 C 的思路,即`struct`就是`struct`,他和`int`就是不一样的,没有必要通过各种运算符重载来制造一种"幻觉",模拟`int`。相反,Zig 吸收现代语言中最有用的"元类型",比如`slice`,`tuple`,`tagged union`等作为语言内置的基本类型,从这一点上对 C 进行增强。虽然这样降低了语言的表现力,但是却简化了设计,降低了"心智负担"。
 
-比如 Zig 里的`tuple`,C++里也有`std::tuple`。当然,`std::tuple`是通过一系列的模板元编程的方式实现的,但是这个在 Zig 里是内置的,因此写代码时出现语法错误,Zig 可以直接告诉你是`tuple`用的不对,但是 C++则会打印很多错误日志。再比如`optional`,C++里也有`std::optinonal`,Zig 里只用`?T`。C++里有`std::variant`,而 Zig 里有`tagged union`。当然我们可以说,C++因为具备了这种元能力,当语法不足够"甜"时,我们可以发明新的轮子,但是代价就是系统愈发的复杂。而 Zig 则持续保持简单。
+比如 Zig 里的`tuple`,C++ 里也有`std::tuple`。当然,`std::tuple`是通过一系列的模板元编程的方式实现的,但是这个在 Zig 里是内置的,因此写代码时出现语法错误,Zig 可以直接告诉你是`tuple`用的不对,但是 C++ 则会打印很多错误日志。再比如`optional`,C++ 里也有`std::optinonal`,Zig 里只用`?T`。C++ 里有`std::variant`,而 Zig 里有`tagged union`。当然我们可以说,C++ 因为具备了这种元能力,当语法不足够"甜"时,我们可以发明新的轮子,但是代价就是系统愈发的复杂。而 Zig 则持续保持简单。
 
-不过话说回来,很多底层系统的开发需求往往和这种类型系统的构建相悖,比如如果你的类型就是一个`int`的封装,那么即使发生拷贝你也无所谓性能开销。但是如果是一个`struct`,那么通常情况下,你会比较 care 拷贝,而可能考虑"移动"之类的手段。这个时候各种 C++的提供的幻觉,就成了程序员开发的绊脚石,经常你需要分析一段 C++表达式里到底有没有发生拷贝,他是左值还是右值,其实你在写 C 语言的时候也很少去考虑了这些,你在 Zig 里同样也不需要。
+不过话说回来,很多底层系统的开发需求往往和这种类型系统的构建相悖,比如如果你的类型就是一个`int`的封装,那么即使发生拷贝你也无所谓性能开销。但是如果是一个`struct`,那么通常情况下,你会比较 care 拷贝,而可能考虑"移动"之类的手段。这个时候各种 C++ 的提供的幻觉,就成了程序员开发的绊脚石,经常你需要分析一段 C++ 表达式里到底有没有发生拷贝,他是左值还是右值,其实你在写 C 语言的时候也很少去考虑了这些,你在 Zig 里同样也不需要。
 
 ## [类型系统]($heading.id('type-system'))
 
-C 语言最大弊病就是没有提供标准库,C++的标准库你要是能看懂,得具备相当的 C++的语法知识,但是 Zig 的标准库几乎不需要文档就能看懂。这其实是因为,在 C++里,类型不是一等成员(first class member),因此实现一些模版元编程算法特别不直观。但是在 Zig 里,`type`就是一等成员,比如你可以写:
+C 语言最大弊病就是没有提供标准库,C++ 的标准库你要是能看懂,得具备相当的 C++ 的语法知识,但是 Zig 的标准库几乎不需要文档就能看懂。这其实是因为,在 C++ 里,类型不是一等成员 (first class member),因此实现一些模版元编程算法特别不直观。但是在 Zig 里,`type`就是一等成员,比如你可以写:
 
 ```zig
 const x: type = u32;
 ```
 
-即,把一个`type`当成一个变量使用。但是 C++里如何来实现这一行代码呢?其实是如下:
+即,把一个`type`当成一个变量使用。但是 C++ 里如何来实现这一行代码呢?其实是如下:
 
 ```cpp
 using x = uint32_t;
@@ -46,7 +46,7 @@ using x = uint32_t;
 fn Some(comptime InputType: type) type
 ```
 
-即输入一个类型,输出一个新类型,那么 C++里对应的东西是啥呢?
+即输入一个类型,输出一个新类型,那么 C++ 里对应的东西是啥呢?
 
 ```cpp
 template 
@@ -55,7 +55,7 @@ struct Some {
 }
 ```
 
-相比之下, Zig 直观太多。那么很自然的,计算一个类型,Zig 里就是调用函数,而 C++则是模板类实例化,然后访问类成员。
+相比之下,Zig 直观太多。那么很自然的,计算一个类型,Zig 里就是调用函数,而 C++ 则是模板类实例化,然后访问类成员。
 
 ```cpp
 Some::OutputType
@@ -81,7 +81,7 @@ struct Fn {
 };
 ```
 
-从这里 C++代码可以感觉出,其实你是拿着尺子,对照着基础模式,然后通过模版偏特化来实现不同分支的逻辑。
+从这里 C++ 代码可以感觉出,其实你是拿着尺子,对照着基础模式,然后通过模版偏特化来实现不同分支的逻辑。
 
 ```cpp
 Fn sizeof(B), A, B>::OutputType
@@ -102,7 +102,7 @@ fn Fn(comptime A:type, comptime B: type) type {
 
 这段代码和普通的 [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) 逻辑没什么区别,特殊的地方在于操作的对象是『类型』。
 
-我们再来看递归的列子。比如有一个类型的 list,我们需要返回其中第 N 个 type。同样,由于在 C++中,类型不是一等成员,因此我们不可能有一个`vector`的东东。那怎么办呢?方法就是直接把`type list`放在模板的参数列表里:`typename ...T`。
+我们再来看递归的列子。比如有一个类型的 list,我们需要返回其中第 N 个 type。同样,由于在 C++ 中,类型不是一等成员,因此我们不可能有一个`vector`的东东。那怎么办呢?方法就是直接把`type list`放在模板的参数列表里:`typename ...T`。
 
 于是,我们写出"函数原型":
 
@@ -161,7 +161,7 @@ pub fn main() void {
 }
 ```
 
-即这个也是完全命令式的。当然 C++20 之后也出现了`if constexpr`和`concept`来进一步简化模版元编程,C++的元编程也在向命令式的方向进化。
+即这个也是完全命令式的。当然 C++20 之后也出现了`if constexpr`和`concept`来进一步简化模版元编程,C++ 的元编程也在向命令式的方向进化。
 
 ## [结束语]($heading.id('conclusion'))
 
diff --git a/content/post/2024-05-07-package-hash.smd b/content/post/2024-05-07-package-hash.smd
index b4647af..db5b752 100644
--- a/content/post/2024-05-07-package-hash.smd
+++ b/content/post/2024-05-07-package-hash.smd
@@ -41,18 +41,18 @@
 - **哈希**
 - 类型为字符串。
 - **[多重哈希](https://multiformats.io/multihash/)**
-  该哈希值是基于一系列文件内容计算得出的,这些文件是在获取URL后并应用了路径规则后得到的。
+  该哈希值是基于一系列文件内容计算得出的,这些文件是在获取 URL 后并应用了路径规则后得到的。
   这个字段是最重要的;一个包是的唯一性是由它的哈希值确定的,不同的 URL 可能对应同一个包。
 
 ## [多重哈希]($heading.id('multiple-hashing'))
 
-在他们的网站上有一个很好的可视化展示,说明了这一过程: [多重哈希](https://multiformats.io/multihash/)。
+在他们的网站上有一个很好的可视化展示,说明了这一过程:[多重哈希](https://multiformats.io/multihash/)。
 
 [multihash 示意图]($image.siteAsset('images/zon-multihash.webp'))
 
-因此 `build.zig.zon` 中的哈希字段不仅包含了摘要(digest),还包含了一些元数据(metadata)。但即使我们丢弃了头部信息,得到的结果仍与下载的 `tar` 包的 `sha256` 摘要不相符。而这就涉及到了包含规则的问题。
+因此 `build.zig.zon` 中的哈希字段不仅包含了摘要 (digest),还包含了一些元数据 (metadata)。但即使我们丢弃了头部信息,得到的结果仍与下载的 `tar` 包的 `sha256` 摘要不相符。而这就涉及到了包含规则的问题。
 
-## [包含规则(inclusion rules)]($heading.id('inclusion-rules'))
+## [包含规则 (inclusion rules)]($heading.id('inclusion-rules'))
 
 回到 [doc/build.zig.zon.md](https://github.com/ziglang/zig/blob/1a6485d111d270014f57c46c53597a516a24dc47/doc/build.zig.zon.md) 文件,我们看到:
 
diff --git a/content/post/2024-05-24-interface-idioms.smd b/content/post/2024-05-24-interface-idioms.smd
index d156e3b..8155f0d 100644
--- a/content/post/2024-05-24-interface-idioms.smd
+++ b/content/post/2024-05-24-interface-idioms.smd
@@ -6,7 +6,7 @@
 .draft = false,
 ---
 
-> 原文链接: https://zig.news/yglcode/code-study-interface-idiomspatterns-in-zig-standard-libraries-4lkj
+> 原文链接:https://zig.news/yglcode/code-study-interface-idiomspatterns-in-zig-standard-libraries-4lkj
 
 ## [引言]($heading.id('introduction'))
 
@@ -22,7 +22,7 @@ Zig 标准库应用了一些代码习语或模式以达到类似效果。
 这里有一些显著的不同:
 
 - 在 Go 中,接口的定义与实现是独立的。可以在任何位置给一个类型实现新接口,只需保证其方法签名与新接口一致即可。无需像在 Java 中那样,需要回过头去修改类型定义,来实现新的接口。
-- Go 的接口只包含用于动态分派的 `vtab`,并且推荐 vtable 中方法即可能少 ,例如 `io.Reader` 和 `io.Writer`只有一个方法。
+- Go 的接口只包含用于动态分派的 `vtab`,并且推荐 vtable 中方法即可能少,例如 `io.Reader` 和 `io.Writer`只有一个方法。
   常见的工具函数如`io.Copy`、`CopyN`、`ReadFull`、`ReadAtLeast` 等,作为包函数提供,内部使用这些小接口。
   与之相比,Zig 的接口,如 `std.mem.Allocator`,同时包含 `vtable` 和一些工具方法,因此方法会多一些。
 
@@ -86,7 +86,7 @@ fn init_data() struct { point: Point, box: Box, circle: Circle } {
 }
 ```
 
-## [接口1:枚举标签联合]($heading.id('interface-1-enumerate-label-unions'))
+## [接口 1:枚举标签联合]($heading.id('interface-1-enumerate-label-unions'))
 
 Loris Cro 在[“使用 Zig 0.10.0 轻松实现接口”](https://zig.news/kristoff/easy-interfaces-with-zig-0100-2hc5)
 中介绍了使用枚举标签联合作为接口的方法。这是最简单的解决方案,尽管你必须在联合类型中显式列出所有“实现”该接口的变体类型。
@@ -126,7 +126,7 @@ test "union_as_intf" {
 }
 ```
 
-## [接口2:vtable 和动态分派的第一种实现]($heading.id('interface-2-the-first-implementation-of-vtable-and-dynamic-dispatch'))
+## [接口 2:vtable 和动态分派的第一种实现]($heading.id('interface-2-the-first-implementation-of-vtable-and-dynamic-dispatch'))
 
 Zig 已从最初基于嵌入式 `vtab` 和 `#fieldParentPtr()` 的动态分派切换到基于“胖指针”接口的以下模式;
 请查阅此文章了解更多细节[“Allocgate 将在 Zig 0.9 中到来...”](https://pithlessly.github.io/allocgate.html)。
@@ -136,7 +136,7 @@ Zig 已从最初基于嵌入式 `vtab` 和 `#fieldParentPtr()` 的动态分派
 
 ```zig
 const Shape2 = struct {
-    // 定义接口字段: ptr,vtab
+    // 定义接口字段:ptr,vtab
     ptr: *anyopaque, //ptr to instance
     vtab: *const VTab, //ptr to vtab
     const VTab = struct {
@@ -198,7 +198,7 @@ test "vtab1_as_intf" {
 }
 ```
 
-## [接口3:vtable 和动态分派的第二种实现]($heading.id('interface-3-second-implementation-of-vtable-and-dynamic-dispatch'))
+## [接口 3:vtable 和动态分派的第二种实现]($heading.id('interface-3-second-implementation-of-vtable-and-dynamic-dispatch'))
 
 在上述第一种实现中,通过 `Shape2.init()` 将 `Box` “转换”为接口 `Shape2` 时,会对 `box` 实例进行类型检查,
 以确保其实现了 `Shape2` 的方法(包括名称的匹配签名)。第二种实现中有两个变化:
@@ -271,7 +271,7 @@ test "vtab2_as_intf" {
 }
 ```
 
-## [接口4:使用嵌入式 vtab 和 @fieldParentPtr() 的原始动态分派]($heading.id('interface-4-raw-dynamic-dispatch-using-embedded-vtab-and-fieldparentptr'))
+## [接口 4:使用嵌入式 vtab 和 @fieldParentPtr() 的原始动态分派]($heading.id('interface-4-raw-dynamic-dispatch-using-embedded-vtab-and-fieldparentptr'))
 
 接口 `std.build.Step` 和所有构建步骤 `std.build.[RunStep, FmtStep, ...]` 仍然使用这种模式。
 
@@ -364,7 +364,7 @@ test "vtab3_embedded_in_struct" {
 }
 ```
 
-## [接口5:编译时的泛型接口]($heading.id('interface-5-compile-time-generic-interface'))
+## [接口 5:编译时的泛型接口]($heading.id('interface-5-compile-time-generic-interface'))
 
 所有上述接口都侧重于 `vtab` 和动态分派:接口值将隐藏其持有的具体值的类型。因此,你可以将这些接口值放入数组中并统一处理。
 
diff --git a/content/post/2024-06-10-zig-hashmap-1.smd b/content/post/2024-06-10-zig-hashmap-1.smd
index edd08e8..28593b5 100644
--- a/content/post/2024-06-10-zig-hashmap-1.smd
+++ b/content/post/2024-06-10-zig-hashmap-1.smd
@@ -8,9 +8,9 @@
 
 > 阅读这篇文章的前提是了解 [Zig 的范型实现](https://www.openmymind.net/learning_zig/generics/)
 
-如大多数哈希映射实现一样,Zig 的 `std.HashMap` 依赖于两个函数:`hash(key: K) u64` 和 `eql(key_a: K, key_b: K) bool`。其中,哈希函数接收一个键并返回一个无符号的64位整数作为哈希码。相同的关键字总是会返回相同的哈希码。然而,为了处理不同的键可能生成相同哈希码的情况(即碰撞),我们还需要 `eql` 函数来确定两个键是否相等。
+如大多数哈希映射实现一样,Zig 的 `std.HashMap` 依赖于两个函数:`hash(key: K) u64` 和 `eql(key_a: K, key_b: K) bool`。其中,哈希函数接收一个键并返回一个无符号的 64 位整数作为哈希码。相同的关键字总是会返回相同的哈希码。然而,为了处理不同的键可能生成相同哈希码的情况(即碰撞),我们还需要 `eql` 函数来确定两个键是否相等。
 
-这是一些标准做法,但Zig的实现有一些特定的细节值得关注。尤其是考虑到标准库中包含多种哈希映射类型以及文档似乎不完整且令人困惑这一点。具体来说,有六种哈希映射变体:`std.HashMap`, `std.HashMapUnmanaged`, `std.AutoHashMap`, `std.AutoHashMapUnmanaged`, `std.StringHashMap`, 和 `std.StringHashMapUnmanaged`。
+这是一些标准做法,但 Zig 的实现有一些特定的细节值得关注。尤其是考虑到标准库中包含多种哈希映射类型以及文档似乎不完整且令人困惑这一点。具体来说,有六种哈希映射变体:`std.HashMap`, `std.HashMapUnmanaged`, `std.AutoHashMap`, `std.AutoHashMapUnmanaged`, `std.StringHashMap`, 和 `std.StringHashMapUnmanaged`。
 
 `std.HashMapUnmanaged` 包含了实现的主要部分。其他五个都是对它的简单包装。由于这些变体通过一个名为“unmanaged”的字段进行包装,因此这五种类型的文档处理不清晰。
 
@@ -26,7 +26,7 @@ pub fn put(self: *Self, key: K, value: V) Allocator.Error!void {
 
 ## [Unmanaged]($heading.id('unmanaged'))
 
-在Zig标准库中随处可见的类型命名约定是 `unmanaged`。这种命名方式表明所涉及的类型不维护 `allocator`。任何需要分配内存的方法都会显式地将 `allocator` 作为参数传递。要实际看到这一点,可以考虑下面这个链表的例子:
+在 Zig 标准库中随处可见的类型命名约定是 `unmanaged`。这种命名方式表明所涉及的类型不维护 `allocator`。任何需要分配内存的方法都会显式地将 `allocator` 作为参数传递。要实际看到这一点,可以考虑下面这个链表的例子:
 
 ```zig
 pub fn LinkedList(comptime T: type) type {
@@ -145,7 +145,7 @@ pub fn LinkedList(comptime T: type) type {
 
 ## [HashMap 与 AutoHashMap]($heading.id('hashmap-and-autohashmap'))
 
-std.HashMap 是一个泛型类型,它接受两个类型参数:键的类型和值的类型。正如我们所见,哈希映射需要两个函数:hash 和 eql。这两个函数合起来被称为“上下文(context)”。这两个函数都作用于键,并且没有一个单一的 hash 或 eql 函数适用于所有类型。例如,对于整数键,eql 将是 `a_key == b_key`;而对于 `[]const u8` 键,我们希望使用 `std.mem.eql(u8, a_key, b_key)`。
+std.HashMap 是一个泛型类型,它接受两个类型参数:键的类型和值的类型。正如我们所见,哈希映射需要两个函数:hash 和 eql。这两个函数合起来被称为“上下文 (context)”。这两个函数都作用于键,并且没有一个单一的 hash 或 eql 函数适用于所有类型。例如,对于整数键,eql 将是 `a_key == b_key`;而对于 `[]const u8` 键,我们希望使用 `std.mem.eql(u8, a_key, b_key)`。
 
 当我们使用 std.HashMap 时,我们需要提供上下文(这两个函数)。我们不久后将讨论这一点,但现在我们可以依赖 std.AutoHashMap,它为我们自动生成这些函数。可能会让你惊讶的是,AutoHashMap 甚至可以为更复杂的键生成上下文。以下操作是有效的:
 以下是修正后的代码:
@@ -195,7 +195,7 @@ pub fn main() !void {
 }
 ```
 
-得到下面的错误:
+得到下面的错误:
 
 > error: `std.auto_hash.autoHash` does not allow slices here (`[]const u8`) because the intent is unclear. Consider using `std.StringHashMap` for hashing the contents of `[]const u8`. Alternatively, consider using `std.auto_hash.hash` or providing your own hash function instead.
 
@@ -385,4 +385,4 @@ pub fn hash(_: HashContext, u: User) u64 {
 
 在下一部分,我们将深入探讨哈希表的键和值,它们是如何存储和管理的。
 
-> 原文地址: https://www.openmymind.net/Zigs-HashMap-Part-1/
+> 原文地址:https://www.openmymind.net/Zigs-HashMap-Part-1/
diff --git a/content/post/2024-06-11-zig-hashmap-2.smd b/content/post/2024-06-11-zig-hashmap-2.smd
index 97b9861..bf076ce 100644
--- a/content/post/2024-06-11-zig-hashmap-2.smd
+++ b/content/post/2024-06-11-zig-hashmap-2.smd
@@ -226,9 +226,9 @@ while (it.next()) |value_ptr| {
 
 ## [Keys]($heading.id('keys'))
 
-我们可以开始和结束这一节:我们关于值的所有内容同样适用于键。这是100%正确的,但这在某种程度上不太直观。大多数开发人员很快就能理解,存储在哈希表中的堆分配的 `User` 实例有其自身的生命周期,需要显式管理/释放。但由于某些原因,这对于键来说并不那么明显。
+我们可以开始和结束这一节:我们关于值的所有内容同样适用于键。这是 100% 正确的,但这在某种程度上不太直观。大多数开发人员很快就能理解,存储在哈希表中的堆分配的 `User` 实例有其自身的生命周期,需要显式管理/释放。但由于某些原因,这对于键来说并不那么明显。
 
-像值一样,如果我们的键是原始类型(例如整数),我们不必做任何特别的处理。 原始类型的键直接存储在哈希表的键数组中,因此其生命周期和内存与哈希表绑定。这是一种非常常见的情况。但另一种常见情况是使用 `std.StringHashMap` 的字符串键。这常常让刚接触 Zig 的开发人员感到困惑,但你需要保证字符串键在哈希表使用它们期间始终有效。而且,如果这些键是动态分配的,你需要确保在不再使用时释放它们。这意味着对键进行与值相同的处理。
+像值一样,如果我们的键是原始类型(例如整数),我们不必做任何特别的处理。原始类型的键直接存储在哈希表的键数组中,因此其生命周期和内存与哈希表绑定。这是一种非常常见的情况。但另一种常见情况是使用 `std.StringHashMap` 的字符串键。这常常让刚接触 Zig 的开发人员感到困惑,但你需要保证字符串键在哈希表使用它们期间始终有效。而且,如果这些键是动态分配的,你需要确保在不再使用时释放它们。这意味着对键进行与值相同的处理。
 
 让我们再次可视化我们的哈希表,但这次正确表示一个字符串键:
 
@@ -371,5 +371,5 @@ if (gop.found_existing) {
 
 希望你现在对使用「std.HashMap」、「std.AutoHashMap」和「std.StringHashMap」以及它们的「unmanaged」变体感到更加得心应手。虽然你可能永远不需要提供自己的上下文(例如「hash」和「eql」函数),但了解这是一个选项是有益的。在日常编程中,可视化数据尤其有用,尤其是在使用指针和添加间接层次时。每当我处理 `value_ptr` 或 `key_ptr` 时,我都会想到这些切片以及值或键与这些切片中值或键的实际地址之间的区别。
 
-> 原文地址: https://www.openmymind.net/Zigs-HashMap-Part-2/
+> 原文地址:https://www.openmymind.net/Zigs-HashMap-Part-2/
 
diff --git a/content/post/2024-06-16-leveraging-zig-allocator.smd b/content/post/2024-06-16-leveraging-zig-allocator.smd
index 91c11d8..155f378 100644
--- a/content/post/2024-06-16-leveraging-zig-allocator.smd
+++ b/content/post/2024-06-16-leveraging-zig-allocator.smd
@@ -8,7 +8,7 @@
 
 > 原文地址: 
 
-假设我们想为Zig编写一个 [HTTP服务器库](https://github.com/karlseguin/http.zig)。这个库的核心可能是线程池,用于处理请求。以简化的方式来看,它可能类似于:
+假设我们想为 Zig 编写一个 [HTTP 服务器库](https://github.com/karlseguin/http.zig)。这个库的核心可能是线程池,用于处理请求。以简化的方式来看,它可能类似于:
 
 ```zig
 fn run(worker: *Worker) void {
@@ -43,7 +43,7 @@ fn greet(allocator: Allocator, req: *http.Request, res: *http.Response) !void {
 }
 ```
 
-虽然这是一个正确的方向,但存在明显的问题:分配的问候语从未被释放。我们的`run`函数不能在写回应后就调用`allocator.free(conn.res.body)`,因为在某些情况下,主体可能不需要被释放。我们可以通过使动作必须 `write()` 回应并因此能够`free`它所做的任何分配来结构化API,但这将使得添加一些功能变得不可能,比如支持中间件。
+虽然这是一个正确的方向,但存在明显的问题:分配的问候语从未被释放。我们的`run`函数不能在写回应后就调用`allocator.free(conn.res.body)`,因为在某些情况下,主体可能不需要被释放。我们可以通过使动作必须 `write()` 回应并因此能够`free`它所做的任何分配来结构化 API,但这将使得添加一些功能变得不可能,比如支持中间件。
 
 最佳和最简单的方法是使用 `ArenaAllocator` 。其工作原理很简单:当我们`deinit`时,所有分配都被释放。
 
@@ -60,9 +60,9 @@ fn run(worker: *Worker) void {
 }
 ```
 
-`std.mem.Allocator` 是一个 "[接口](https://www.openmymind.net/Zig-Interfaces/)" ,我们的动作无需更改。 `ArenaAllocator` 对HTTP服务器来说是一个很好的选择,因为它们与请求绑定,具有明确/可理解的生命周期,并且相对短暂。虽然有可能滥用它们,但可以说:使用更多!
+`std.mem.Allocator` 是一个 "[接口](https://www.openmymind.net/Zig-Interfaces/)" ,我们的动作无需更改。 `ArenaAllocator` 对 HTTP 服务器来说是一个很好的选择,因为它们与请求绑定,具有明确/可理解的生命周期,并且相对短暂。虽然有可能滥用它们,但可以说:使用更多!
 
-我们可以更进一步并重用相同的Arena。这可能看起来不太有用,但是请看:
+我们可以更进一步并重用相同的 Arena。这可能看起来不太有用,但是请看:
 
 ```zig
 fn run(worker: *Worker) void {
@@ -79,7 +79,7 @@ fn run(worker: *Worker) void {
 }
 ```
 
-我们将Arena移出了循环,但重要的部分在内部:每个请求后,我们重置了Arena并保留最多8K内存。这意味着对于许多请求,我们无需访问底层分配器(`worker.server.allocator`)。这种方法简化了内存管理。
+我们将 Arena 移出了循环,但重要的部分在内部:每个请求后,我们重置了 Arena 并保留最多 8K 内存。这意味着对于许多请求,我们无需访问底层分配器(`worker.server.allocator`)。这种方法简化了内存管理。
 
 现在想象一下,如果我们不能用 `retain_with_limit` 重置 Arena,我们还能进行同样的优化吗?可以,我们可以创建自己的分配器,首先尝试使用固定缓冲区分配器(FixedBufferAllocator),如果分配适配,回退到 Arena 分配器。
 
@@ -121,29 +121,29 @@ const FallbackAllocator = struct {
 };
 ```
 
-我们的`alloc`实现首先尝试使用我们定义的"主"分配器进行分配。如果失败,我们会使用"备用"分配器。作为`std.mem.Allocator`接口的一部分,我们需要实现的`resize`方法会确定正在尝试扩展内存的所有者,并然后调用其`rawResize`方法。为了保持代码简单,我在这里省略了`free`方法的具体实现——在这种特定情况下是可以接受的,因为我们计划使用"主"分配器作为`FixedBufferAllocator`,而"备用"分配器则会是`ArenaAllocator`(因此所有释放操作会在arena的`deinit`或`reset`时进行)。
+我们的`alloc`实现首先尝试使用我们定义的"主"分配器进行分配。如果失败,我们会使用"备用"分配器。作为`std.mem.Allocator`接口的一部分,我们需要实现的`resize`方法会确定正在尝试扩展内存的所有者,并然后调用其`rawResize`方法。为了保持代码简单,我在这里省略了`free`方法的具体实现——在这种特定情况下是可以接受的,因为我们计划使用"主"分配器作为`FixedBufferAllocator`,而"备用"分配器则会是`ArenaAllocator`(因此所有释放操作会在 arena 的`deinit`或`reset`时进行)。
 
 接下来我们需要改变我们的`run`方法以利用这个新的分配器:
 
 ```zig
 fn run(worker: *Worker) void {
-    const allocator = worker.server.allocator; // 这是FixedBufferAllocator底层的内存
-    const buf = try allocator.alloc(u8, 8192); // 分配8K字节的内存用于存储数据
+    const allocator = worker.server.allocator; // 这是 FixedBufferAllocator 底层的内存
+    const buf = try allocator.alloc(u8, 8192); // 分配 8K 字节的内存用于存储数据
     defer allocator.free(buf); // 完成后释放内存
 
-    var fba = std.heap.FixedBufferAllocator.init(buf); // 初始化FixedBufferAllocator
+    var fba = std.heap.FixedBufferAllocator.init(buf); // 初始化 FixedBufferAllocator
 
     while (queue.pop()) |conn| {
-        defer fba.reset(); // 重置FixedBufferAllocator,准备处理下一个请求
+        defer fba.reset(); // 重置 FixedBufferAllocator,准备处理下一个请求
 
-        var arena = std.heap.ArenaAllocator.init(allocator); // 初始化ArenaAllocator用于分配额外内存
+        var arena = std.heap.ArenaAllocator.init(allocator); // 初始化 ArenaAllocator 用于分配额外内存
         defer arena.deinit();
 
         var fallback = FallbackAllocator{
             .fba = &fba,
             .primary = fba.allocator(),
             .fallback = arena.allocator(),
-        }; // 创建FallbackAllocator,包含FixedBufferAllocator和ArenaAllocator
+        }; // 创建 FallbackAllocator,包含 FixedBufferAllocator 和 ArenaAllocator
 
         const action = worker.route(conn.req.url); // 路由请求到对应的动作处理函数
         action(fallback.allocator(), conn.req, conn.res) catch { // 处理动作执行中的错误 };
@@ -153,7 +153,7 @@ fn run(worker: *Worker) void {
 }
 ```
 
-这种方法实现了类似于在`retain_with_limit`中重置arena的功能。我们创建了一个可以重复使用的`FixedBufferAllocator`,用于处理每个请求的8K字节内存需求。由于一个动作可能需要更多的内存,我们仍然需要`ArenaAllocator`来提供额外的空间。通过将`FixedBufferAllocator`和`ArenaAllocator`包裹在我们的`FallbackAllocator`中,我们可以确保任何分配都首先尝试使用(非常快的)`FixedBufferAllocator`,当其空间用尽时,则会切换到`ArenaAllocator`。
+这种方法实现了类似于在`retain_with_limit`中重置 arena 的功能。我们创建了一个可以重复使用的`FixedBufferAllocator`,用于处理每个请求的 8K 字节内存需求。由于一个动作可能需要更多的内存,我们仍然需要`ArenaAllocator`来提供额外的空间。通过将`FixedBufferAllocator`和`ArenaAllocator`包裹在我们的`FallbackAllocator`中,我们可以确保任何分配都首先尝试使用(非常快的)`FixedBufferAllocator`,当其空间用尽时,则会切换到`ArenaAllocator`。
 
 我们通过暴露`std.mem.Allocator`接口,可以调整如何工作而不破坏`greet`。这不仅简化了资源管理(例如通过`ArenaAllocator`),而且通过重复使用分配来提高了性能(类似于我们做的`retain_with_limit`或`FixedBufferAllocator`的操作)。
 
diff --git a/content/post/2024-08-12-zoop.smd b/content/post/2024-08-12-zoop.smd
index 5f38305..f49ed6b 100644
--- a/content/post/2024-08-12-zoop.smd
+++ b/content/post/2024-08-12-zoop.smd
@@ -8,7 +8,7 @@
 
 ## [zoop]($heading.id('zoop'))
 
-zoop 是 zig 的一个 OOP 解决方案,详细信息可以看看 [zoop官网](https://zhuyadong.github.io/zoop-docs/)。
+zoop 是 zig 的一个 OOP 解决方案,详细信息可以看看 [zoop 官网](https://zhuyadong.github.io/zoop-docs/)。
 
 ## [为什么不用别的 OOP 语言]($heading.id('why-not-use-another-oop-language'))
 
@@ -25,7 +25,7 @@ pub const Base = struct {
 }
 ```
 
-2-3行是一个struct成为zoop类必需的两行,这样一来,`Base` 就成为了一个 zoop 的类。
+2-3 行是一个 struct 成为 zoop 类必需的两行,这样一来,`Base` 就成为了一个 zoop 的类。
 
 创建 `Base` 的对象有两种方法:
 
@@ -185,7 +185,7 @@ var base: *Base = child.cast(Base);
 try t.expectEqualStrings(child.getName(), "override");
 try t.expectEqualStrings(base.getName(), "Child");
 
-// 通过接口调用(虚函数调用) getName();
+// 通过接口调用 (虚函数调用) getName();
 try t.expectEqualStrings(child.as(IGetName).?.getName(), "override");
 try t.expectEqualStrings(base.as(IGetName).?.getName(), "override");
 ```
@@ -275,12 +275,12 @@ pub const Child = struct {
 }
 ```
 
-接口有两个:
+接口有两个:
 
 - `IGetName`: 接口方法 `getName`
 - `ISetName`: 接口方法 `setName`
 
-类有两个:
+类有两个:
 
 - `Base`: 基类,实现接口 `ISetName`
 - `Child`: 子类,继承 `Base`,并实现接口 `IGetName`
@@ -331,7 +331,7 @@ zoop.Mixin(Child) = struct {
 
 事实上,`child.mixin.data.mymod_Base.mixin.meta` 里面的内容就是完全复制的 `child.mixin.meta`,因为所有内层对象的 `mixin.meta` 都是复制的最外层那个对象的 `mixin.meta`,因而所有对象的 `rootptr` 都指向最外层对象,这也是为什么叫 `rootptr` 的原因。
 
-再看看 `typeinfo` 字段,这个字段是一个有3个字段的结构:
+再看看 `typeinfo` 字段,这个字段是一个有 3 个字段的结构:
 
 - `typename`: 这是 `rootptr` 指向对象的类型名
 - `getVtable`: 根据接口名获得接口 `Vtable` 的函数
@@ -349,7 +349,7 @@ OOP 概念中的继承,重写,虚函数,实质其实就是在编译时动
 
 ### [`struct` 很万能]($heading.id('struct-is-omnipotent'))
 
-`comptime` 编程中,`struct` 是你最好的朋友,想在不同的 `comptime` 函数之间传递数据,最方便的方式,就是通过构造一个 `struct`,把想传递的数据通过 `pub const xxx = ...` 的方式传递出去,通过 `struct` 保存数据最好的地方,就在于这个数据在运行时也是可用的 (`struct` 中的常量,是保存在 exe 的 `.data` 区,运行时可见),[zoop.tuple](https://zhuyadong.github.io/zoop-docs/reference/tuple) 就是通过这个方法实现的。
+`comptime` 编程中,`struct` 是你最好的朋友,想在不同的 `comptime` 函数之间传递数据,最方便的方式,就是通过构造一个 `struct`,把想传递的数据通过 `pub const xxx = ...` 的方式传递出去,通过 `struct` 保存数据最好的地方,就在于这个数据在运行时也是可用的(`struct` 中的常量,是保存在 exe 的 `.data` 区,运行时可见),[zoop.tuple](https://zhuyadong.github.io/zoop-docs/reference/tuple) 就是通过这个方法实现的。
 
 ### [动态构造 `struct` 的字段,用 `@Type()`]($heading.id('dynamically-construct-the-fields-of-struct-using-type'))
 
@@ -430,7 +430,7 @@ struct {
 }
 ```
 
-通过返回一个 `struct` 的方式,在它的 `value` 常量中保存了一个 `tuple`,`tuple` 有一个带有方法 `setName` 的 `struct` 元素。众所周知,`tuple` 是可以各种组合的 (参考 [zoop.tuple](https://zhuyadong.github.io/zoop-docs/reference/tuple)),于是 zoop 通过 [zoop.Fn](https://zhuyadong.github.io/zoop-docs/reference/zoop#Fn),比如上例中 `Child` 中的 `pub usingnamespace zoop.Fn(@This())`,把 `Child` 类型代入 `Base.Fn` 中,就相当于在 `Child` 内写了如下代码:
+通过返回一个 `struct` 的方式,在它的 `value` 常量中保存了一个 `tuple`,`tuple` 有一个带有方法 `setName` 的 `struct` 元素。众所周知,`tuple` 是可以各种组合的(参考 [zoop.tuple](https://zhuyadong.github.io/zoop-docs/reference/tuple)),于是 zoop 通过 [zoop.Fn](https://zhuyadong.github.io/zoop-docs/reference/zoop#Fn),比如上例中 `Child` 中的 `pub usingnamespace zoop.Fn(@This())`,把 `Child` 类型代入 `Base.Fn` 中,就相当于在 `Child` 内写了如下代码:
 
 ```zig
 pub usingnamespace struct {
@@ -459,7 +459,7 @@ fn typeId(comptime T: type) u32 {
 利用这个新的 `typeId()` 函数,zoop 怎么做动态类型转换,我从 zoop 中抄一段代码大家一看就明白:
 
 ```zig
-/// 返回一个函数,函数的功能是输入接口 `typeid`,返回针对T的该接口的 Vtable
+/// 返回一个函数,函数的功能是输入接口 `typeid`,返回针对 T 的该接口的 Vtable
 fn getVtableFunc(comptime T: type) VtableFunc {
     // 先找出 T 实现的所有接口
     const ifaces = tuple.Append(.{IObject}, Interfaces(T)).value;
diff --git a/content/post/2025-01-23-bonkers-comptime.smd b/content/post/2025-01-23-bonkers-comptime.smd
index 7362b64..bb075ea 100644
--- a/content/post/2025-01-23-bonkers-comptime.smd
+++ b/content/post/2025-01-23-bonkers-comptime.smd
@@ -6,7 +6,7 @@
 .draft = false,
 ---
 
-> 原文: 
+> 原文:
 
 > 译注:原文中的代码块是交互式,翻译时并没有移植。另外,由于 comptime 本身即是关键概念,并且下文的意思更侧重于 Zig comptime 的特性,故下文大多使用 comptime 代替编译时概念。
 
@@ -20,7 +20,7 @@
 
 为了明确起见,所有示例都是有效的 Zig 代码,但示例中的转换只是概念性的,它们并不是 Zig 实际的实现方式。
 
-## [视角0: 忽略它]($heading.id('perspective-0-ignore-it'))
+## [视角 0: 忽略它]($heading.id('perspective-0-ignore-it'))
 
 我说我喜欢这个特性,却又立刻叫你忽略它,这确实有点怪。但我认为此处正是 Zig comptime 威力所体现的地方,所以我将从这里出发。Zig Zen 中的第三条是“倾向于阅读代码,而不是编写代码。”确实,能够轻松地阅读代码在各种情况下都很重要,因为它是建立概念理解的基础,而这种理解也是调试或修改代码所必需的。
 
@@ -71,7 +71,7 @@ pub fn main() void {
 
 Zig 中有很多基于 comptime 且远远不止这样简单的类型反射,但你只需要阅读那些代码、完全无需深入了解其中有关 comptime 的细节就可以理解它们在干什么。当然,如果你想使用 comptime 编写代码,则不能仅仅止步于此,让我们继续深入。
 
-## [视角1: 泛型]($heading.id('perspective-1-generics'))
+## [视角 1: 泛型]($heading.id('perspective-1-generics'))
 
 泛型在 Zig 中并不是一个特定的功能。相反,Zig 中的仅仅一小部分的 comptime 特性就可以提供用来处理你进行泛型编程所需的一切。这种视角虽然不能让你完全理解 comptime,但它确实为你提供了一个入口点,借此,你可以完成基于元编程的许多任务。
 
@@ -119,11 +119,11 @@ pub fn main() void {
 }
 ```
 
-当然,也可以通过使用特殊类型 anytype 来推断参数的类型,而这通常在参数的类型对函数签名的其余部分没有影响时使用。(译注:此时要限制 a, b, c 的类型相同,所以此处不用 anytype )
+当然,也可以通过使用特殊类型 anytype 来推断参数的类型,而这通常在参数的类型对函数签名的其余部分没有影响时使用。(译注:此时要限制 a, b, c 的类型相同,所以此处不用 anytype)
 
-## [视角2:编译时运行的标准代码]($heading.id('perspective-2-standard-code-running-at-compile-time'))
+## [视角 2:编译时运行的标准代码]($heading.id('perspective-2-standard-code-running-at-compile-time'))
 
-这是一个古老的故事: 增加一种自动执行命令的方法。当然,你还需要变量。 哦,还有条件。 拜托,能给我循环吗?这些看似合理的需求,最终导致这些自动化命令变得越来越复杂,甚至演变成一个完整的宏语言。 但 Zig 不同, 在运行时、编译时,甚至是构建系统中都使用了相同的语言。
+这是一个古老的故事:增加一种自动执行命令的方法。当然,你还需要变量。哦,还有条件。拜托,能给我循环吗?这些看似合理的需求,最终导致这些自动化命令变得越来越复杂,甚至演变成一个完整的宏语言。但 Zig 不同,在运行时、编译时,甚至是构建系统中都使用了相同的语言。
 
 考虑经典的 Fizz Buzz。
 
@@ -149,7 +149,7 @@ pub fn main() !void {
 }
 ```
 
-确实很简单。但是,每当讨论如何优化 Fizz Buzz 算法时,人们总是忽略一个事实:标准的 Fizz Buzz 问题只需要输出前100个数字的结果。既然输出是固定的,那为什么不直接预先计算出答案,然后输出呢?(由此,我时常认为那些有关优化讨论有些滑稽的。)
+确实很简单。但是,每当讨论如何优化 Fizz Buzz 算法时,人们总是忽略一个事实:标准的 Fizz Buzz 问题只需要输出前 100 个数字的结果。既然输出是固定的,那为什么不直接预先计算出答案,然后输出呢?(由此,我时常认为那些有关优化讨论有些滑稽的。)
 我们可以使用相同的 Fizz Buzz 函数来实现这一点。
 
 ```zig
@@ -178,7 +178,7 @@ pub fn main() !void {
 
 comptime 和运行时之间有一些小的区别。比如,只有 comptime 可以访问类型为 comptime_int、comptime_float 或 type 的变量。此外,一些函数只有 comptime 参数,这使它们仅限于编译时环境。相对的,只有运行时才能进行系统调用和那些依赖系统调用的函数。如果你的代码不使用这些特性,那么它在编译时和运行时中的表现将是一样的。
 
-## [视角3:程序特化]($heading.id('perspective-3-partial-evaluation'))
+## [视角 3:程序特化]($heading.id('perspective-3-partial-evaluation'))
 - [原文地址](https://en.wikipedia.org/wiki/Partial_application)
 
 > 译者注:程序特化(Partial Evaluation)是一种编译优化技术,主要是:在编译期预先计算部分表达式或代码路径,以减少运行时计算开销,提前生成更具体的代码实现。
@@ -198,7 +198,7 @@ pub fn main() void {
     for (array) |value| {
         sum += value;
     }
-    // 这可以展开为:
+    // 这可以展开为:
     {
         const value = array[0];
         sum += value;
@@ -216,7 +216,7 @@ pub fn main() void {
 }
 ```
 
-程序特化是一种可以向函数传递部分(但不一定是全部)参数的技术。 在这种情况下,可以对只使用已知值的表达式进行替换。 这样就产生了一个新函数,它只接受仍然未知的参数。 comtime 可以看作是在编译过程中进行的部分求值。 再看一下 sum 结构的例子,我们就会发现:
+程序特化是一种可以向函数传递部分(但不一定是全部)参数的技术。在这种情况下,可以对只使用已知值的表达式进行替换。这样就产生了一个新函数,它只接受仍然未知的参数。comtime 可以看作是在编译过程中进行的部分求值。再看一下 sum 结构的例子,我们就会发现:
 
 ```zig
 onst MyStruct = struct {
@@ -230,7 +230,7 @@ onst MyStruct = struct {
           sum += @field(my_struct, field_name);
       }
 
-    // 这可以展开为:
+    // 这可以展开为:
     {
         const field_name = "a";
         sum += @field(my_struct, field_name);
@@ -253,14 +253,14 @@ onst MyStruct = struct {
 };
 ```
 
-上面的示例是我们手动展开后的示例,但这项工作是由 Zig 的 comptime 完成的。这使得我们可以直接独立而完整地编写出我们要实现的功能,而不需要添加"当你改变 `MyStruct` 的字段时,记得更新 sum 函数"这样的由于依赖于 `MyStruct` 具体字段而预防功能失效的注释。
+上面的示例是我们手动展开后的示例,但这项工作是由 Zig 的 comptime 完成的。这使得我们可以直接独立而完整地编写出我们要实现的功能,而不需要添加"当你改变 `MyStruct` 的字段时,记得更新 sum 函数"这样的由于依赖于 `MyStruct` 具体字段而预防功能失效的注释。
 基于 comptime 的版本在 `MyStruct` 的任何字段变更时都可以正确地自动处理。
 
-## [视角4:Comptime 求值,运行时代码生成]($heading.id('perspective-4-comptime-evaluation-runtime-code-generation'))
+## [视角 4:Comptime 求值,运行时代码生成]($heading.id('perspective-4-comptime-evaluation-runtime-code-generation'))
 
-这与程序特化(Partial Evaluation)非常相似。这里有两个版本的代码,输入(编译前)和输出(编译后)。输入代码由编译器运行。如果一个语句在编译时是可知的,它就会被直接求值。但是如果一个语句需要某些运行时的值,那么这个语句就会被添加到输出代码中。
+这与程序特化(Partial Evaluation)非常相似。这里有两个版本的代码,输入 (编译前) 和输出 (编译后)。输入代码由编译器运行。如果一个语句在编译时是可知的,它就会被直接求值。但是如果一个语句需要某些运行时的值,那么这个语句就会被添加到输出代码中。
 
-让我们以数组求和为例来说明这个过程:
+让我们以数组求和为例来说明这个过程:
 
 > 输入这一段代码:
 
@@ -302,15 +302,15 @@ const MyStruct = struct {
 
 自然,这样做的后果是死代码永远不会被语义分析。也就是说,一个无效的函数并不总是会在实际被使用之前产出相应的编译错误。(对此你可能需要适应一段时间)然而,这也使得编译更加高效(译注:部分地弥补了 Zig 暂不支持增量编译的缺陷),并允许更自然的外观条件编译,这里没有 `#ifdef` (译注:谢天谢地~)!
 
-值得注意的是, comptime 在 Zig 的设计中是个很基本的设计, 所有的 Zig 代码都通过这个虚拟机运行,包括没有明显使用 comptime 的函数。 即使是简单的类型名称,如函数参数,实际上也是在 comptime 中评估类型变量的表达式。 这就是上面泛型示例的工作原理。 这也意味着您可以酌情使用更复杂的表达式来计算类型。
+值得注意的是,comptime 在 Zig 的设计中是个很基本的设计,所有的 Zig 代码都通过这个虚拟机运行,包括没有明显使用 comptime 的函数。即使是简单的类型名称,如函数参数,实际上也是在 comptime 中评估类型变量的表达式。这就是上面泛型示例的工作原理。这也意味着您可以酌情使用更复杂的表达式来计算类型。
 
-这样做的另一个后果是,Zig 代码的静态分析要比大多数静态类型语言复杂得多,因为编译器需要运行很大一部分才能确定所有类型。 因此,在 Zig 工具链跟上之前,代码自动补全等编辑工具并不总是能很好地发挥作用。
+这样做的另一个后果是,Zig 代码的静态分析要比大多数静态类型语言复杂得多,因为编译器需要运行很大一部分才能确定所有类型。因此,在 Zig 工具链跟上之前,代码自动补全等编辑工具并不总是能很好地发挥作用。
 
-## [视角5:直接生成代码(Textual Code Generation)]($heading.id('perspective-5-direct-code-generation'))
+## [视角 5:直接生成代码(Textual Code Generation)]($heading.id('perspective-5-direct-code-generation'))
 
-我在文章开头感叹元编程难度。然而,即使在 Zig 中,它仍然是一个强大的工具,在解决某些问题方面也占有一席之地。如果您熟悉这种元编程方法,对 Zig comptime 提供的功能可能会觉得有些残缺。比如, 怎么在写一段代码在运行时能够生成新代码?
+我在文章开头感叹元编程难度。然而,即使在 Zig 中,它仍然是一个强大的工具,在解决某些问题方面也占有一席之地。如果您熟悉这种元编程方法,对 Zig comptime 提供的功能可能会觉得有些残缺。比如,怎么在写一段代码在运行时能够生成新代码?
 
-但等等,上一个例子不就是这样吗? 如果你以正确的方式看待问题,写代码的代码和混合运行时代码之间存在着潜在的等价关系。
+但等等,上一个例子不就是这样吗?如果你以正确的方式看待问题,写代码的代码和混合运行时代码之间存在着潜在的等价关系。
 
 下有两例。第一个是一个元编程的示例,第二个是我们熟悉的 comptime 示例。这两个版本的代码有着相同的逻辑。
 
@@ -361,13 +361,13 @@ pub fn writeMyStructOfType(
 }
 ```
 
-以上 struct 字段的生成体现了上述两种转换方式,并且将两者混合在了一行中。 字段的类型表达式由生成器/运行时完成,而字段本身则作为运行时代码使用的定义。
+以上 struct 字段的生成体现了上述两种转换方式,并且将两者混合在了一行中。字段的类型表达式由生成器/运行时完成,而字段本身则作为运行时代码使用的定义。
 
 在 comptime 下,引用类型名称的方式更加直接,可以直接使用函数,而不必将文本拼接成一个在代码生成中保持一致的名称。
 
-这种观点有一个例外。 您可以创建字段名称在编译时就已确定的类型,但这样做需要调用一个内置函数,该函数包含一个字段定义列表。 因此,您无法在这些类型上定义方法等声明。 在实践中,这并不会限制代码的表达能力,但确实限制了你可以向其他代码公开哪些类型的 API。
+这种观点有一个例外。您可以创建字段名称在编译时就已确定的类型,但这样做需要调用一个内置函数,该函数包含一个字段定义列表。因此,您无法在这些类型上定义方法等声明。在实践中,这并不会限制代码的表达能力,但确实限制了你可以向其他代码公开哪些类型的 API。
 
-与本节相关的是文本宏,如 C 语言中的文本宏。你可以做的大多数正常事情都可以在 comptime 中完成,尽管它们很少采用类似的形式。 不过,文本宏并不能做所有允许做的事情。 例如,你不能决定不喜欢某个 Zig 关键字,然后让宏代替你自己的关键字。 我认为这是一个正确的决定,尽管对于那些习惯了这种能力的人来说,这是一个艰难的过渡。 此外,Zig 参考了半个世纪以来的程序员在这方面的探索,所以它的选择要理智得多。
+与本节相关的是文本宏,如 C 语言中的文本宏。你可以做的大多数正常事情都可以在 comptime 中完成,尽管它们很少采用类似的形式。不过,文本宏并不能做所有允许做的事情。例如,你不能决定不喜欢某个 Zig 关键字,然后让宏代替你自己的关键字。我认为这是一个正确的决定,尽管对于那些习惯了这种能力的人来说,这是一个艰难的过渡。此外,Zig 参考了半个世纪以来的程序员在这方面的探索,所以它的选择要理智得多。
 
 ## [结论]($heading.id('conclusion'))
 
diff --git a/content/post/2025-07-19-zine-migration.smd b/content/post/2025-07-19-zine-migration.smd
index d5e1661..e9485f7 100644
--- a/content/post/2025-07-19-zine-migration.smd
+++ b/content/post/2025-07-19-zine-migration.smd
@@ -92,7 +92,7 @@ Scripty 作为表达式语言,其威力在条件和嵌套逻辑中得以体现
 ```html
 

...

``` -在上述代码中,如果页面标题长度大于25个字符,`

` 标签就会被赋予 `long-title` 这个 class。 +在上述代码中,如果页面标题长度大于 25 个字符,`

` 标签就会被赋予 `long-title` 这个 class。 通过这三者的结合,Zine 在保证内容与布局分离的同时,提供了高度的灵活性和安全性。更详细的用法,推荐阅读 Zine 官方文档。 diff --git a/content/post/2025-09-17-new-writer.smd b/content/post/2025-09-17-new-writer.smd index 3f2db5c..9279936 100644 --- a/content/post/2025-09-17-new-writer.smd +++ b/content/post/2025-09-17-new-writer.smd @@ -114,7 +114,7 @@ fn typeErasedWriteFn(context: *const anyopaque, bytes: []const u8) anyerror!usiz 这是一个使用 `File.Writer` 通过 `AnyWriter` 进行指针关系的可视化图: -[图形:File.Writer和AnyWriter的指针关系图]($image.siteAsset('images/AnyWriter-Diagram.svg')) +[图形:File.Writer 和 AnyWriter 的指针关系图]($image.siteAsset('images/AnyWriter-Diagram.svg')) `File` 和 `AnyWriter` 都是运行时对象,`AnyWriter` 使用 `File.Writer` 提供的 `write` 函数。 @@ -203,13 +203,13 @@ pub const VTable = struct { **虚表和 @fieldParentPtr** -任何动态调度的接口都需要两件事:一种访问实现状态的方法,以及一组操作该状态以完成接口任务的函数指针。这组函数指针统称为虚表,或 “vtable”。`AnyWriter` 只有一个直接存储的函数指针。`Io.Writer` 有一个更大的接口,因此存储了一个指向包含 4 个函数指针的虚表的指针。 +任何动态调度的接口都需要两件事:一种访问实现状态的方法,以及一组操作该状态以完成接口任务的函数指针。这组函数指针统称为虚表,或“vtable”。`AnyWriter` 只有一个直接存储的函数指针。`Io.Writer` 有一个更大的接口,因此存储了一个指向包含 4 个函数指针的虚表的指针。 在 `AnyWriter` 中,指向实现对象的类型擦除指针存储在接口中。在新的 `Io.Writer` 中,接口是实现对象的一个字段,并且指向该字段的指针被传递给函数。通过使用 Zig 内置的 `@fieldParentPtr` 访问虚表函数中的实现状态,该函数从字段的指针中减去该字段在结构体内的偏移量。 这是一个帮助您理解这一切的图: -[图形: File.Writer 指针关系图]($image.siteAsset('images/Io-Writer-Diagram.svg')) +[图形:File.Writer 指针关系图]($image.siteAsset('images/Io-Writer-Diagram.svg')) 查看 `File.Writer` 实现,注意接口是如何存储为一个字段的。像 `drain` 这样的接口函数接受一个指向该字段的指针,并使用 `@fieldParentPtr` 获取指向 `File.Writer` 的指针。 @@ -253,7 +253,7 @@ pub const Writer = struct { 我们一直看到的 `drain` 函数是实现实际写入缓冲数据的方式。用户完成 Writer 操作后,他们调用 `flush` 来写出缓冲区中剩余的任何数据。 -[图形: 展示 Io.Writer 如何缓存数据]($image.siteAsset('images/Buffering-Interface.svg')) +[图形:展示 Io.Writer 如何缓存数据]($image.siteAsset('images/Buffering-Interface.svg')) 在大多数语言中,缓冲是在后台完成的实现细节。Zig pre-writergate 具有 `std.io.BufferedWriter`,它为此目的提供了一个 `GenericWriter`。以下是如何使用它: @@ -357,7 +357,7 @@ fn writeData(writer: *std.Io.Writer) std.Io.Writer.Error!void { 默认的 `rebase` 通过重复调用 `drain` 以在保留区域之前写出字节,然后将保留的字节向后复制以保持连续来工作。这是一个小规模的示例,说明它可能是什么样子: -[图形: rebase 在小缓冲区上的工作方式]($image.siteAsset('images/Rebase-Diagram.svg')) +[图形:rebase 在小缓冲区上的工作方式]($image.siteAsset('images/Rebase-Diagram.svg')) **结论** diff --git a/content/post/2025-09-18-new-writer.smd b/content/post/2025-09-18-new-writer.smd index a335861..533b861 100644 --- a/content/post/2025-09-18-new-writer.smd +++ b/content/post/2025-09-18-new-writer.smd @@ -7,40 +7,40 @@ > 原文:https://www.openmymind.net/Zigs-New-Writer/ -正如你可能听说过的,Zig的`Io`命名空间正在重新设计。最终,这将意味着重新引入异步。作为第一步,Writer和Reader接口以及一些相关代码已经过改进。 +正如你可能听说过的,Zig 的`Io`命名空间正在重新设计。最终,这将意味着重新引入异步。作为第一步,Writer 和 Reader 接口以及一些相关代码已经过改进。 -> 这篇文章是根据Zig的2025年7月中旬开发版本撰写的。它不适用于Zig 0.14.x(或任何以前的版本),并且可能随着更多Io命名空间的返工而过时。 +> 这篇文章是根据 Zig 的 2025 年 7 月中旬开发版本撰写的。它不适用于 Zig 0.14.x(或任何以前的版本),并且可能随着更多 Io 命名空间的返工而过时。 -不久前,我写了一篇博客文章,[Zig's Writers](https://www.openmymind.net/In-Zig-Whats-a-Writer/)试图解释Zig的 Writer 。充其量,我会将当前状态描述为“混淆”两个 Writer 界面,同时经常处理`anytype`. .和虽然`anytype`很方便,它缺乏开发人员人体工程学。此外,目前的设计对于一些常见情况存在重大性能问题。 +不久前,我写了一篇博客文章,[Zig's Writers](https://www.openmymind.net/In-Zig-Whats-a-Writer/)试图解释 Zig 的 Writer。充其量,我会将当前状态描述为“混淆”两个 Writer 界面,同时经常处理`anytype`. .和虽然`anytype`很方便,它缺乏开发人员人体工程学。此外,目前的设计对于一些常见情况存在重大性能问题。 -新`Writer`接口是`std.Io.Writer`. .至少,实现必须提供`drain`功能。其签名看起来像: +新`Writer`接口是`std.Io.Writer`. .至少,实现必须提供`drain`功能。其签名看起来像: ```zig fn drain(w: *Writer, data: []const []const u8, splat: usize) Error!usize ``` -你可能会惊讶于这是自定义编写者需要实现的方法。它不仅需要一个字符串数组,但那是什么`splat`参数?像我一样,你可能期望一个更简单的`write`方法: +你可能会惊讶于这是自定义编写者需要实现的方法。它不仅需要一个字符串数组,但那是什么`splat`参数?像我一样,你可能期望一个更简单的`write`方法: ```zig fn write(w: *Writer, data: []const u8) Error!usize ``` -事实证明`std.Io.Writer`有内置的缓冲。例如,如果我们想要一个`Writer`为 A`std.fs.File`我们需要提供缓冲: +事实证明`std.Io.Writer`有内置的缓冲。例如,如果我们想要一个`Writer`为 A`std.fs.File`我们需要提供缓冲: ```zig var buffer: [1024]u8 = undefined; var writer = my_file.writer(&buffer); ``` -当然,如果我们不想要缓冲,我们总能传递一个空的缓冲区: +当然,如果我们不想要缓冲,我们总能传递一个空的缓冲区: ```zig var writer = my_file.writer(&.{}); ``` -这就解释了为什么自定义编写者需要实现一个`drain`方法,而不是更简单的东西,如`write`. . +这就解释了为什么自定义编写者需要实现一个`drain`方法,而不是更简单的东西,如`write`. . -最简单的实现方法`drain`,在进行这次更大的大修时,Zig标准库已经升级了很多,是: +最简单的实现方法`drain`,在进行这次更大的大修时,Zig 标准库已经升级了很多,是: ```zig fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) !usize { @@ -50,15 +50,15 @@ fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) !usize { } ``` -我们忽略了`splat`参数,只需在中写第一个值`data`(`data.len > 0`保证是真实的)。这 转`drain`进入什么更简单`write`方法会看起来像。因为我们返回写入的字节长度,`std.Io.Writer`会知道我们可能没有写所有数据并调用`drain`再次,如果有必要,与其余的数据。 +我们忽略了`splat`参数,只需在中写第一个值`data`(`data.len > 0`保证是真实的)。这 转`drain`进入什么更简单`write`方法会看起来像。因为我们返回写入的字节长度,`std.Io.Writer`会知道我们可能没有写所有数据并调用`drain`再次,如果有必要,与其余的数据。 > 如果你被调用混淆了`@fieldParentPtr`[upcoming linked list changes](https://www.openmymind.net/Zigs-New-LinkedList-API/)查看我关于即将到来的链接列表更改的帖子。 -实际执行`drain`为`File`是一个非平凡的〜150行代码。它具有特定于平台的代码,[并在可能的情况下利用矢量I/O。](https://www.openmymind.net/TCP-Server-In-Zig-Part-3-Minimizing-Writes-and-Reads/#writev)显然,提供简单的实现或更优化的实现具有灵活性。 +实际执行`drain`为`File`是一个非平凡的〜150 行代码。它具有特定于平台的代码,[并在可能的情况下利用矢量 I/O。](https://www.openmymind.net/TCP-Server-In-Zig-Part-3-Minimizing-Writes-and-Reads/#writev)显然,提供简单的实现或更优化的实现具有灵活性。 -就像当前状态,当你做`file.writer(&buffer)`你没有得到一个`std.Io.Writer`. .相反,你得到一个`File.Writer`. .获取实际`std.Io.Writer`,你需要访问`interface`领域。这只是一个惯例,但期望它在整个标准和第三方图书馆中使用。准备好看很多`&xyz.interface`打电话! +就像当前状态,当你做`file.writer(&buffer)`你没有得到一个`std.Io.Writer`. .相反,你得到一个`File.Writer`. .获取实际`std.Io.Writer`,你需要访问`interface`领域。这只是一个惯例,但期望它在整个标准和第三方图书馆中使用。准备好看很多`&xyz.interface`打电话! -这种简化`File`显示三种类型之间的关系: +这种简化`File`显示三种类型之间的关系: ```zig pub const File = struct { @@ -85,15 +85,15 @@ pub const File = struct { } ``` -实例`File.Writer`需要存在于某个地方(例如在堆栈上),因为那是`std.Io.Writer`接口存在。有可能 那个`File`可以直接有一个`writer_interface: std.Io.Writer`字段,但这会限制你每个文件一个写入器,并且会膨胀`File`结构。 +实例`File.Writer`需要存在于某个地方 (例如在堆栈上),因为那是`std.Io.Writer`接口存在。有可能 那个`File`可以直接有一个`writer_interface: std.Io.Writer`字段,但这会限制你每个文件一个写入器,并且会膨胀`File`结构。 -我们可以从上面看到,当我们调用`Writer`一个“界面”,它只是一个正常的结构。它有几个领域超越`buffer`和`vtable.drain`,但这些是唯一两个具有非默认值;我们必须提供它们。因`Writer`接口实现了很多典型的“ Writer ”行为,比如`writeAll`和`print`(用于格式化写作)。它还有多种方法,只有`Writer`实施可能会关心。例如,`File.Writer.drain`必须调用`consume`以便作者的内部状态可以更新。在文档中并排列出所有这些功能,起初让我感到困惑。希望这是文档生成有朝一日能够帮助解开的东西。 +我们可以从上面看到,当我们调用`Writer`一个“界面”,它只是一个正常的结构。它有几个领域超越`buffer`和`vtable.drain`,但这些是唯一两个具有非默认值;我们必须提供它们。因`Writer`接口实现了很多典型的“Writer”行为,比如`writeAll`和`print`(用于格式化写作)。它还有多种方法,只有`Writer`实施可能会关心。例如,`File.Writer.drain`必须调用`consume`以便作者的内部状态可以更新。在文档中并排列出所有这些功能,起初让我感到困惑。希望这是文档生成有朝一日能够帮助解开的东西。 -新`Writer`已经接管了多种方法。例如,`std.fmt.formatIntBuf`已经不存在了。替代者是`printInt`方法`Writer`. .但这需要一个`Writer`实例而不是简单`[]u8`以前要求。 +新`Writer`已经接管了多种方法。例如,`std.fmt.formatIntBuf`已经不存在了。替代者是`printInt`方法`Writer`. .但这需要一个`Writer`实例而不是简单`[]u8`以前要求。 -很容易错过,但`Writer.fixed([]u8) Writer`函数是你正在寻找的。您将将此用于迁移到的任何函数`Writer`用来在A上工作`buffer: []u8`. . +很容易错过,但`Writer.fixed([]u8) Writer`函数是你正在寻找的。您将将此用于迁移到的任何函数`Writer`用来在 A 上工作`buffer: []u8`. . -迁移时,_可能会遇到以下错误:在“...”中没有名为“adaptToNewApi”的字段或成员函数。_你可以看到为什么发生这种情况,通过查看更新的实现`std.fmt.format`: +迁移时,_可能会遇到以下错误:在“...”中没有名为“adaptToNewApi”的字段或成员函数。_你可以看到为什么发生这种情况,通过查看更新的实现`std.fmt.format`: ```zig pub fn format(writer: anytype, comptime fmt: []const u8, args: anytype) !void { @@ -104,8 +104,8 @@ pub fn format(writer: anytype, comptime fmt: []const u8, args: anytype) !void { } ``` -因为这个功能被移动到`std.Io.Writer`, 任何`writer`传入`format`必须能够升级到新接口。这是完成的,同样,只是惯例,通过让“老” Writer 揭露一个`adaptToNewApi`返回一个类型的方法,它暴露了一个`new_interface: std.Io.Writer`领域。这是很容易实现使用基本`drain`实现,你可以在标准库中找到一些例子,但如果你不控制传统作者,那就没什么帮助了。 +因为这个功能被移动到`std.Io.Writer`, 任何`writer`传入`format`必须能够升级到新接口。这是完成的,同样,只是惯例,通过让“老”Writer 揭露一个`adaptToNewApi`返回一个类型的方法,它暴露了一个`new_interface: std.Io.Writer`领域。这是很容易实现使用基本`drain`实现,你可以在标准库中找到一些例子,但如果你不控制传统作者,那就没什么帮助了。 -我犹豫是否要对这一变化发表意见。我不懂语言设计。然而,虽然我认为这是对当前API的改进,但我一直认为直接添加缓冲到`Writer`不是理想的。 +我犹豫是否要对这一变化发表意见。我不懂语言设计。然而,虽然我认为这是对当前 API 的改进,但我一直认为直接添加缓冲到`Writer`不是理想的。 -我认为大多数语言都通过作文处理缓冲。你把一个 Reader / Writer ,并把它包装在缓冲阅读器或缓冲器。这种方法似乎既简单易懂,又易于实施,同时具有强大功能。它可以应用于缓冲和IO之外的东西。Zig似乎在与这种模式作斗争。而不是为此类问题提供一种有凝聚力和通用的方法,而是将一个特定API(IO)的特定特征(缓冲)融入到标准库中。也许我太密集了,无法理解,或者未来的变化会更全面地解决这个问题。 +我认为大多数语言都通过作文处理缓冲。你把一个 Reader / Writer ,并把它包装在缓冲阅读器或缓冲器。这种方法似乎既简单易懂,又易于实施,同时具有强大功能。它可以应用于缓冲和 IO 之外的东西。Zig 似乎在与这种模式作斗争。而不是为此类问题提供一种有凝聚力和通用的方法,而是将一个特定 API(IO) 的特定特征 (缓冲) 融入到标准库中。也许我太密集了,无法理解,或者未来的变化会更全面地解决这个问题。 diff --git a/content/post/2026-01-18-shipping-zig-libraries-with-c-abi.smd b/content/post/2026-01-18-shipping-zig-libraries-with-c-abi.smd index e5c7106..07e9a0d 100644 --- a/content/post/2026-01-18-shipping-zig-libraries-with-c-abi.smd +++ b/content/post/2026-01-18-shipping-zig-libraries-with-c-abi.smd @@ -1,5 +1,5 @@ --- -.title = "在0.15.2版本使用C ABI模拟Zig ABI", +.title = "在 0.15.2 版本使用 C ABI 模拟 Zig ABI", .date = @date("2026-01-18T21:05:00+0800"), .author = "艾达爱白糖", .layout = "post.shtml", @@ -9,9 +9,9 @@ }, --- -白糖:艾达,我怎么才能把我的zig项目发布出去,但是不公开源码呀? +白糖:艾达,我怎么才能把我的 zig 项目发布出去,但是不公开源码呀? -艾达:可以分发二进制库文件和相关符号定义文件(符合C ABI的符号定义),照着这样写就行了。 +艾达:可以分发二进制库文件和相关符号定义文件(符合 C ABI 的符号定义),照着这样写就行了。 ```zig // lib.zig @@ -31,9 +31,9 @@ pub const Container = extern struct { }; ``` -白糖:导出为C库呀。但是你这就一个结构,一个函数,这么写当然没什么问题,如果有很多函数,结构呢? +白糖:导出为 C 库呀。但是你这就一个结构,一个函数,这么写当然没什么问题,如果有很多函数,结构呢? -艾达:别忘了zig的`comptime`机制呀,编译时可以自动导出符号并且生成符号定义文件。 +艾达:别忘了 zig 的`comptime`机制呀,编译时可以自动导出符号并且生成符号定义文件。 ```zig // lib.zig @@ -68,7 +68,7 @@ fn exports(Target: type) void { 白糖:这里只有导出,那符号定义文件怎么自动生成。而且编译时必须是常量,如果可以把调用了`exports`的类型都收集起来给运行时用就好了。艾达,有什么办法吗? -艾达:编译时收集有些困难。不过办法还是有的,如果`exports`在编译时和生成符号定义文件时是不同的函数是不是就可以解决了。接下来就是zig构建系统的事情,我们要利用其module机制。 +艾达:编译时收集有些困难。不过办法还是有的,如果`exports`在编译时和生成符号定义文件时是不同的函数是不是就可以解决了。接下来就是 zig 构建系统的事情,我们要利用其 module 机制。 艾达:首先我们把项目分成多个模块,分别是库本身、导出符号模块、生成符号定义文件模块和依赖生成文件模块的库本身以及运行生成符号定义文件的模块。 @@ -120,7 +120,7 @@ fn main() void { } ``` -艾达:最后在build.zig中创建依赖。 +艾达:最后在 build.zig 中创建依赖。 ```=html
@@ -216,9 +216,9 @@ pub fn build2(b: *std.Build) void {
 }
 ```
 
-艾达:现在完成了构建库时自动生成相关符号定义文件,实现了分享zig库不公开源码的功能。因为用C ABI所以会有一些限制,例如zig标准库的一些类型不能导出,需要实现C版本的。
+艾达:现在完成了构建库时自动生成相关符号定义文件,实现了分享 zig 库不公开源码的功能。因为用 C ABI 所以会有一些限制,例如 zig 标准库的一些类型不能导出,需要实现 C 版本的。
 
-艾达:具体生成的函数这里省略了,就是递归类型的`decls`和`fields`字段根据类型输出相应文本。不过zig的`Type`有些限制,生成会有些不完美,比如函数信息就没有参数名,`struct`内的`union`字段不知道初始化的哪个变体等。
+艾达:具体生成的函数这里省略了,就是递归类型的`decls`和`fields`字段根据类型输出相应文本。不过 zig 的`Type`有些限制,生成会有些不完美,比如函数信息就没有参数名,`struct`内的`union`字段不知道初始化的哪个变体等。
 
 白糖:太好了,有什么例子可以看看吗?
 
@@ -252,7 +252,7 @@ pub const Color = extern struct {
 };
 ```
 
-艾达:对了,如果不想在构建库的同时生成符号定义文件可以修改build.zig。
+艾达:对了,如果不想在构建库的同时生成符号定义文件可以修改 build.zig。
 
 ```zig
     // ...
diff --git a/content/post/news/2023-12-27-second-meetup.smd b/content/post/news/2023-12-27-second-meetup.smd
index 3300320..75fb30b 100644
--- a/content/post/news/2023-12-27-second-meetup.smd
+++ b/content/post/news/2023-12-27-second-meetup.smd
@@ -31,7 +31,7 @@ action,主要是同步了不同项目的进展,由于临近年底,大家
 - [原文地址](https://github.com/learnzig/learnzig)
 
 - 主要参与人员:金中甲
-- zig的进阶特性,诸如构建系统、包管理、与C交互均已完成,目前教程内容已基本覆盖日常使用
+- zig 的进阶特性,诸如构建系统、包管理、与 C 交互均已完成,目前教程内容已基本覆盖日常使用
 - 增加了评论区的功能
 - 待完成:反射(编译期反射和运行时反射)、内建函数说明(包含使用例子)、未定义行为、wasm、原子操作这些边缘部分
 
@@ -60,13 +60,13 @@ action,主要是同步了不同项目的进展,由于临近年底,大家
 社区,经过简单筛选,新增一名成员:Reco,下面是他的一些履历:
 
 - 南美 Optimes co.,limited 联合创始人、CTO
-- 任我行软件股份有限公司 集团CTO
+- 任我行软件股份有限公司 集团 CTO
 
 其他技术兴趣经历
 
-1.  图灵出版社区签约作者。4本电子系列书:《Vue.js小书》《Git小书》《HTTP小书》《Swift
-    iOS开发小书》
-2.  微软 DotNet 技术俱乐部 2007-2010年成都地区主席
+1.  图灵出版社区签约作者。4 本电子系列书:《Vue.js 小书》《Git 小书》《HTTP 小书》《Swift
+    iOS 开发小书》
+2.  微软 DotNet 技术俱乐部 2007-2010 年成都地区主席
 3.  
 
 非常欢迎 Reco 的加入!也希望更多对 Zig 感兴趣的朋友加入我们,普及 Zig
diff --git a/content/post/news/2024-04-27-release-party-review.smd b/content/post/news/2024-04-27-release-party-review.smd
index 52c3ef1..93a63b9 100644
--- a/content/post/news/2024-04-27-release-party-review.smd
+++ b/content/post/news/2024-04-27-release-party-review.smd
@@ -33,7 +33,7 @@ Zig 的构建系统分为两部分:
 - zon 文件,声明依赖, `zig fetch` 会去下载里面的依赖
 - `build.zig` 文件,项目的构建器,由多个 Step
   形成一个有向无环图,来驱动不同逻辑的进行,如安装头文件、编译静态链接库等。Step
-  里面最重要的是 Compile ,addTest、addExecutable
+  里面最重要的是 Compile,addTest、addExecutable
   返回的都是它,主要功能是对代码进行编译。其他常见的 Step 还有
   - ConfigHeader 配置要用的头文件
   - InstallArtifact,将编译好的 lib 或 bin 安装到 zig-out 目录中
@@ -44,7 +44,7 @@ longer depend on LLVM, LLD, and Clang libraries
 \#16270](https://github.com/ziglang/zig/issues/16270) 这个 issue
 的基础。之前笔者以为所谓移除 llvm,是把 Zig 代码翻译成 C
 代码,然后再有不同架构下的 C
-编译器来生成最终的可执行文件,目前看这种想法是错误的, 尽管 Zig 有 C
+编译器来生成最终的可执行文件,目前看这种想法是错误的,尽管 Zig 有 C
 这个后端,但目前看并不是解决这个 issue 专用的。
 
 这就不得不好奇,Zig