diff --git a/.gitignore b/.gitignore index fd3340e..1bdc0ce 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ zig-cache/ zig-out/ .cache_ggshield +.vscode +old/ diff --git a/content/contributing.smd b/content/contributing.smd index 2723ae6..087a701 100644 --- a/content/contributing.smd +++ b/content/contributing.smd @@ -11,7 +11,7 @@ Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文 1. 供稿,分享自己使用 Zig 的心得,方式见下文 2. 改进 zigcc 组织下的开源项目,这是 [open issues](https://ask.ziglang.cc/github) -# [供稿方式]($section.id('contribute')) +# [供稿方式]($heading.id('contribute')) 1. Fork 仓库 https://github.com/zigcc/zigcc.github.io 2. 在 `content/post` 内添加自己的文章(smd格式),文件命名为: `${YYYY}-${MM}-${DD}-${SLUG}.smd` @@ -31,7 +31,7 @@ Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文 --- ``` -# [本地预览]($section.id('本地预览')) +# [本地预览]($heading.id('local-preview')) 首先你得安装 `zine`,安装方法见 [Quick Start](https://zine-ssg.io/quickstart/),之后就可以通过执行 `zine` 命令来预览你的文章。 @@ -39,12 +39,12 @@ Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文 zine ``` -# [发布平台]($section.id('publishing-platform')) +# [发布平台]($heading.id('publishing-platform')) - [ZigCC 网站](https://ziglang.cc) - [ZigCC 公众号](https://github.com/zigcc/.github/raw/main/zig_mp.png) -# [网站更新日志]($section.id('changelog')) +# [网站更新日志]($heading.id('changelog')) - **2025-06-30:** 切换到 [zine](https://zine-ssg.io/) - **2024-08-18:** 切换主题 [docsy](https://github.com/google/docsy) diff --git a/content/learn/coding-in-zig.smd b/content/learn/coding-in-zig.smd index 3b6de98..1e733e5 100644 --- a/content/learn/coding-in-zig.smd +++ b/content/learn/coding-in-zig.smd @@ -10,7 +10,7 @@ 在介绍了 Zig 语言的大部分内容之后,我们将对一些主题进行回顾,并展示几种使用 Zig 编程时一些实用的技巧。在此过程中,我们将介绍更多的标准库,并介绍一些稍复杂些的代码片段。 -# [悬空指针 Dangling Pointers]($section.id('dangling-pointers')) +# [悬空指针 Dangling Pointers]($heading.id('dangling-pointers')) 我们首先来看看更多关于悬空指针的例子。这似乎是一个奇怪的问题,但如果你之前主要使用带垃圾回收的语言,这可能是你学习 Zig 最大的障碍。 @@ -105,7 +105,7 @@ const User = struct { 如果把所有东西都放在一个函数中,再加上一个像 `User` 这样的小值,这仍然像是一个人为制造的问题。我们需要一个能让数据所有权成为当务之急的例子。 -# [所有权 Ownership]($section.id('ownership')) +# [所有权 Ownership]($heading.id('ownership')) 我喜欢哈希表(HashMap),因为这是每个人都知道并且会经常使用的结构。它们有很多不同的用例,其中大部分你可能都用过。虽然哈希表可以用在一个短期查找的地方,但通常用于长期查找,因此插入其内的值需要同样长的生命周期。 @@ -218,7 +218,7 @@ defer { 我保证,关于悬挂指针和内存管理的讨论已经结束了。我们所讨论的内容可能还不够清晰或过于抽象。当你有更实际的问题需要解决时,再重新讨论这个问题也不迟。不过,如果你打算编写任何稍具规模(non-trivial)的程序,这几乎肯定是你需要掌握的内容。当你觉得可以的时候,我建议你参考上面这个示例,并自己动手实践一下。引入一个 `UserLookup` 类型来封装我们必须做的所有内存管理。尝试使用 `*User` 代替 `User`,在堆上创建用户,然后像处理键那样释放它们。编写覆盖新结构的测试,使用 `std.testing.allocator` 确保不会泄漏任何内存。 -# [ArrayList]($section.id('arraylist')) +# [ArrayList]($heading.id('arraylist')) 现在你可以忘掉我们的 `IntList` 和我们创建的通用替代方案了。Zig 标准库中有一个动态数组实现:`std.ArrayList(T)`。 @@ -313,7 +313,7 @@ pub fn main() !void { } ``` -# [Anytype]($section.id('anytype')) +# [Anytype]($heading.id('anytype')) 在[语言概述的第一部分](language-overview-1)中,我们简要介绍了 `anytype`。这是一种非常有用的编译时 duck 类型。下面是一个简单的 logger: @@ -383,7 +383,7 @@ fn stringify( 第一个参数 `value: anytype` 是显而易见的,它是要序列化的值,可以是任何类型(实际上,Zig 的 JSON 序列化器不能序列化某些类型,比如 HashMap)。我们可以猜测,`out_stream` 是写入 JSON 的地方,但至于它需要实现什么方法,你和我一样猜得到。唯一的办法就是阅读源代码,或者传递一个假值,然后使用编译器错误作为我们的文档。如果有更好的自动文档生成器,这一点可能会得到改善。不过,我希望 Zig 能提供接口,这已经不是第一次了。 -# [@TypeOf]($section.id('typeof')) +# [@TypeOf]($heading.id('typeof')) 在前面的部分中,我们使用 `@TypeOf` 来帮助我们检查各种变量的类型。从我们的用法来看,你可能会认为它返回的是字符串类型的名称。然而,鉴于它是一个 PascalCase 风格函数,你应该更清楚:它返回的是一个 `type`。 @@ -412,7 +412,7 @@ pub const User = struct { 更常见的是 `@TypeOf` 与 `@typeInfo` 配对,后者返回一个 `std.builtin.Type`。这是一个功能强大的带标签的联合(tagged union),可以完整描述一个类型。`std.json.stringify` 函数会递归地调用它,以确定如何将提供的 `value` 序列化。 -# [构建系统]($section.id('build-system')) +# [构建系统]($heading.id('build-system')) 如果你通读了整本指南,等待着深入了解如何建立更复杂的项目,包括多个依赖关系和各种目标,那你就要失望了。Zig 拥有强大的构建系统,以至于越来越多的非 Zig 项目都在使用它,比如 libsodium。不幸的是,所有这些强大的功能都意味着,对于简单的需求来说,它并不是最容易使用或理解的。 @@ -497,7 +497,7 @@ test "dummy build test" { 这是启动和运行构建系统所需的最低配置。但是请放心,如果你需要构建你的程序,Zig 内置的功能大概率能覆盖你的需求。最后,你可以(也应该)在你的项目根目录下使用 `zig init`,让 Zig 为你创建一个文档齐全的 `build.zig` 文件。 -# [第三方依赖]($section.id('third-party-dependencies')) +# [第三方依赖]($heading.id('third-party-dependencies')) Zig 的内置软件包管理器相对较新,因此存在一些缺陷。虽然还有改进的余地,但它目前还是可用的。我们需要了解两个部分:创建软件包和使用软件包。我们将对其进行全面介绍。 diff --git a/content/learn/heap-memory.smd b/content/learn/heap-memory.smd index 6a8ae6a..f314d90 100644 --- a/content/learn/heap-memory.smd +++ b/content/learn/heap-memory.smd @@ -14,7 +14,7 @@ 本部分分为两个主题。第一个主题是第三个内存区域--堆的总体概述。另一个主题是 Zig 直接而独特的堆内存管理方法。即使你熟悉堆内存,比如使用过 C 语言的 `malloc`,你也会希望阅读第一部分,因为它是 Zig 特有的。 -# [堆]($section.id('heap')) +# [堆]($heading.id('heap')) 堆是我们可以使用的第三个也是最后一个内存区域。与全局数据和调用栈相比,堆有点像蛮荒之地:什么都可以使用。具体来说,在堆中,我们可以在运行时创建大小已知的内存,并完全控制其生命周期。 @@ -52,7 +52,7 @@ fn getRandomCount() !u8 { 一般来说,每次 `alloc` 都会有相应的 `free`。`alloc`分配内存,`free`释放内存。不要让这段简单的代码限制了你的想象力。这种 `try alloc` + `defer free` 的模式很常见,这是有原因的:在我们分配内存的地方附近释放相对来说是万无一失的。但同样常见的是在一个地方分配,而在另一个地方释放。正如我们之前所说,堆没有内置的生命周期管理。你可以在 HTTP 处理程序中分配内存,然后在后台线程中释放,这是代码中两个完全独立的部分。 -# [defer 和 errdefer]($section.id('defer-and-errdefer')) +# [defer 和 errdefer]($heading.id('defer-and-errdefer')) 说句题外话,上面的代码介绍了一个新的语言特性:`defer`,它在退出作用域时执行给定的代码。『作用域退出』包括到达作用域的结尾或从作用域返回。严格来说, `defer` 与分配器或内存管理并无严格关系;你可以用它来执行任何代码。但上述用法很常见。 @@ -100,7 +100,7 @@ pub const Game = struct { > `init` 和 `deinit` 的名字并不特殊。它们只是 Zig 标准库使用的,也是社区采纳的名称。在某些情况下,包括在标准库中,会使用 `open` 和 `close`,或其他更适当的名称。 -# [双重释放和内存泄漏]($section.id('double-free-and-memory-leak')) +# [双重释放和内存泄漏]($heading.id('double-free-and-memory-leak')) 上面提到过,没有规则规定什么时候必须释放什么东西。但事实并非如此,还是有一些重要规则,只是它们不是强制的,需要你自己格外小心。 @@ -159,7 +159,7 @@ fn isSpecial(allocator: Allocator, name: [] const u8) !bool { 至少在双重释放的情况下,我们的程序会遭遇严重崩溃。内存泄漏可能很隐蔽。不仅仅是根本原因难以确定。真正的小泄漏或不常执行的代码中的泄漏甚至很难被发现。这是一个很常见的问题,Zig 提供了帮助,我们将在讨论分配器时看到。 -# [创建与销毁]($section.id('create-and-destroy')) +# [创建与销毁]($heading.id('create-and-destroy')) `std.mem.Allocator`的`alloc`方法会返回一个切片,其长度为传递的第二个参数。如果想要单个值,可以使用 `create` 和 `destroy` 而不是 `alloc` 和 `free`。 @@ -233,7 +233,7 @@ fn init(allocator: std.mem.Allocator, id: u64, power: i32) !*User{ 请记住,`create` 返回一个 `!*User`,所以我们的 `user` 是 `*User` 类型。 -# [分配器 Allocator]($section.id('allocator')) +# [分配器 Allocator]($heading.id('allocator')) Zig 的核心原则之一是无隐藏内存分配。根据你的背景,这听起来可能并不特别。但这与 C 语言中使用标准库的 malloc 函数分配内存的做法形成了鲜明的对比。在 C 语言中,如果你想知道一个函数是否分配内存,你需要阅读源代码并查找对 malloc 的调用。 @@ -254,7 +254,7 @@ defer allocator.free(say); 如果你正在构建一个库,那么最好接受一个 `std.mem.Allocator`,然后让库的用户决定使用哪种分配器实现。否则,你就需要选择正确的分配器,正如我们将看到的,这些分配器并不相互排斥。在你的程序中创建不同的分配器可能有很好的理由。 -# [通用分配器 GeneralPurposeAllocator]($section.id('general-purpose-allocator')) +# [通用分配器 GeneralPurposeAllocator]($heading.id('general-purpose-allocator')) 顾名思义,`std.heap.GeneralPurposeAllocator` 是一种通用的、线程安全的分配器,可以作为应用程序的主分配器。对于许多程序来说,这是唯一需要的分配器。程序启动时,会创建一个分配器并传递给需要它的函数。我的 HTTP 服务器库中的示例代码就是一个很好的例子: @@ -299,7 +299,7 @@ var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 类型是什么,字段在哪里?类型其实是 `std.heap.general_purpose_allocator.Config`,但它并没有直接暴露出来,这也是我们没有显式给出类型的原因之一。没有设置字段是因为 Config 结构定义了默认值,我们将使用默认值。这是配置、选项的中常见的模式。事实上,我们在下面几行向 `init` 传递 `.{.port = 5882}` 时又看到了这种情况。在本例中,除了端口这一个字段外,我们都使用了默认值。 -# [std.testing.allocator]($section.id('std-testing-allocator')) +# [std.testing.allocator]($heading.id('std-testing-allocator')) 希望当我们谈到内存泄漏时,你已经足够烦恼,而当我提到 Zig 可以提供帮助时,你肯定渴望了解更多这方面内容。这种帮助来自 `std.testing.allocator`,它是一个 `std.mem.Allocator` 实现。目前,它基于通用分配器(GeneralPurposeAllocator)实现,并与 Zig 的测试运行器进行了集成,但这只是实现细节。重要的是,如果我们在测试中使用 `std.testing.allocator`,就能捕捉到大部分内存泄漏。 @@ -423,7 +423,7 @@ self.allocator.free(self.items); 将`items`复制到我们的 `larger` 切片中后, 添加最后一行`free`可以解决泄漏的问题。如果运行 `zig test learning.zig`,便不会再有错误。 -# [ArenaAllocator]($section.id('arena-allocator')) +# [ArenaAllocator]($heading.id('arena-allocator')) 通用分配器(GeneralPurposeAllocator)是一个合理的默认设置,因为它在所有可能的情况下都能很好地工作。但在程序中,你可能会遇到一些固定场景,使用更专业的分配器可能会更合适。其中一个例子就是需要在处理完成后丢弃的短期状态。解析器(Parser)通常就有这样的需求。一个 `parse` 函数的基本轮廓可能是这样的 @@ -508,7 +508,7 @@ defer list.deinit(); 最后举个简单的例子,我上面提到的 HTTP 服务器在响应中暴露了一个 `ArenaAllocator`。一旦发送了响应,它就会被清空。由于`ArenaAllocator`的生命周期可以预测(从请求开始到请求结束),因此它是一种高效的选择。就性能和易用性而言,它都是高效的。 -# [固定缓冲区分配器 FixedBufferAllocator]($section.id('fixed-buffer-allocator')) +# [固定缓冲区分配器 FixedBufferAllocator]($heading.id('fixed-buffer-allocator')) 我们要讨论的最后一个分配器是 `std.heap.FixedBufferAllocator`,它可以从我们提供的缓冲区(即 `[]u8`)中分配内存。这种分配器有两大好处。首先,由于所有可能使用的内存都是预先创建的,因此速度很快。其次,它自然而然地限制了可分配内存的数量。这一硬性限制也可以看作是一个缺点。另一个缺点是,`free` 和 `destroy` 只对最后分配/创建的项目有效(想想堆栈)。调用释放非最后分配的内存是安全的,但不会有任何作用。 diff --git a/content/learn/index.smd b/content/learn/index.smd index 9578631..d859049 100644 --- a/content/learn/index.smd +++ b/content/learn/index.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [《学习 Zig》 目录]($section.id('table-of-contents')) +# [《学习 Zig》 目录]($heading.id('table-of-contents')) - [前言](./preface) - [安装 Zig](./installing-zig) @@ -24,7 +24,7 @@ 初次接触 Zig 的用户可以按序号依次阅读,对于有经验的 Zig 开发者可按需阅读感兴趣的章节。 -# [关于原作者]($section.id('about-original-author')) +# [关于原作者]($heading.id('about-original-author')) [Karl Seguin](https://www.linkedin.com/in/karlseguin/) 在多个领域有着丰富经验,前微软 MVP,他撰写了大量文章,是多个微软公共新闻组的活跃成员。现居新加坡。他还是以下教程的作者: @@ -34,17 +34,17 @@ 可以在 找到他的博客,或者通过 [@karlseguin](http://twitter.com/karlseguin) 在 Twitter 上关注他。 -# [翻译原则]($section.id('translation-principles')) +# [翻译原则]($heading.id('translation-principles')) 技术文档的翻译首要原则是准确,但在准确的前提下如何保证『信、达、雅』?这是个挑战,在翻译本教程时,在某些情况下会根据上下文进行意译,便于中文读者阅读。 最后,感谢翻译者的无私贡献。❤️️ -# [离线阅读]($section.id('offline-reading')) +# [离线阅读]($heading.id('offline-reading')) 在本仓库的 [release 页面](https://github.com/zigcc/zigcc.github.io/releases)会定期将本教程导出为 PDF 格式,读者可按需下载。 -# [其他学习资料]($section.id('other-learning-resources')) +# [其他学习资料]($heading.id('other-learning-resources')) 由于 Zig 目前还处于快速迭代,因此最权威的资料无疑是官方的 [Zig Language Reference](https://ziglang.org/documentation/master/),遇到语言的细节问题,基本都可以在这里找到答案。其次是社区的一些高质量教程,例如: diff --git a/content/learn/language-overview-1.smd b/content/learn/language-overview-1.smd index 76ce6b0..60e5330 100644 --- a/content/learn/language-overview-1.smd +++ b/content/learn/language-overview-1.smd @@ -37,7 +37,7 @@ pub const User = struct { > 请参阅[安装 Zig 部分](installing-zig),以便快速启动并运行它。 -# [模块引用]($section.id('module-reference')) +# [模块引用]($heading.id('module-reference')) 很少有程序是在没有标准库或外部库的情况下以单个文件编写的。我们的第一个程序也不例外,它使用 Zig 的标准库来进行打印输出。 Zig 的模块系统非常简单,只依赖于 `@import` 函数和 `pub` 关键字(使代码可以在当前文件外部访问)。 @@ -88,7 +88,7 @@ const MAX_POWER = user.MAX_POWER; - 如何导入其他文件 - 如何导出变量、函数定义 -# [代码注释]($section.id('code-comment')) +# [代码注释]($heading.id('code-comment')) 下面这行 Zig 代码是一个注释: @@ -100,7 +100,7 @@ Zig 没有像 C 语言中类似 `/* ... */` 的多行注释。 基于注释的文档自动生成功能正在试验中。如果你看过 Zig 的标准库文档,你就会看到它的实际应用。`//!` 被称为顶级文档注释,可以放在文件的顶部。三斜线注释 (`///`) 被称为文档注释,可以放在特定位置,如声明之前。如果在错误的地方使用这两种文档注释,编译器都会出错。 -# [函数]($section.id('function')) +# [函数]($heading.id('function')) 下面这行 Zig 代码是程序的入口函数 `main`: @@ -146,7 +146,7 @@ fn add(a: i64, b: i64) i64 { 为了提高可读性,Zig 中不支持函数重载(用不同的参数类型或参数个数定义的同名函数)。暂时来说,以上就是我们需要了解的有关函数的全部内容。 -# [结构体]($section.id('struct')) +# [结构体]($heading.id('struct')) 下面这行代码创建了一个 `User` 结构体: @@ -266,7 +266,7 @@ pub fn init(name: []const u8, power: u64) User { 就像我们迄今为止已经探索过的大多数东西一样,今后在讨论 Zig 语言的其他部分时,我们会再次讨论结构体。不过,在大多数情况下,它们都是简单明了的。 -# [数组和切片]($section.id('array-slice')) +# [数组和切片]($heading.id('array-slice')) 我们可以略过代码的最后一行,但鉴于我们的代码片段包含两个字符串 `"Goku"` 和 `{s}'s power is {d}\n`,你可能会对 Zig 中的字符串感到好奇。为了更好地理解字符串,我们先来了解一下数组和切片。 @@ -365,7 +365,7 @@ pub fn main() void { 在了解 Zig 语言的其他方面(尤其是字符串)的同时,我们还将发现更多有关数组和切片的知识。 -# [字符串]($section.id('string')) +# [字符串]($heading.id('string')) 我希望我能说,Zig 里有字符串类型,而且非常棒。遗憾的是,它没有。最简单来说,字符串是字节(u8)的序列(即数组或切片)。实际上,我们可以从 `name` 字段的定义中看到这一点:`name: []const u8`. @@ -399,7 +399,7 @@ pub fn main() void { 当然,在实际程序中,大多数字符串(以及更通用的数组)在编译时都是未知的。最典型的例子就是用户输入,程序编译时并不知道用户输入。这一点我们将在讨论内存时再次讨论。但简而言之,对于这种在编译时不能确定值的数据(长度当然也就无从得知),我们将在运行时动态分配内存。我们的字符串变量(仍然是 `[]const u8` 类型)将是指向动态分配的内存的切片。 -# [comptime 和 anytype]($section.id('comptime-and-anytype')) +# [comptime 和 anytype]($heading.id('comptime-and-anytype')) 在我们未解释的最后一行代码中,涉及的知识远比表面看到的多: diff --git a/content/learn/language-overview-2.smd b/content/learn/language-overview-2.smd index ad735aa..15787b6 100644 --- a/content/learn/language-overview-2.smd +++ b/content/learn/language-overview-2.smd @@ -10,7 +10,7 @@ 本部分继续上一部分的内容:熟悉 Zig 语言。我们将探究 Zig 的控制流和结构以外的类型。通过这两部分的学习,我们将掌握 Zig 语言的大部分语法,这让我们可以继续深入 Zig 语言,同时也为如何使用 std 标准库打下了基础。 -# [控制流]($section.id('control-flow')) +# [控制流]($heading.id('control-flow')) Zig 的控制流很可能是我们所熟悉的,但它与 Zig 语言的其他特性协同工作是我们还没有探索过。我们先简单概述控制流的基本使用,之后在讨论依赖控制流的相关特性时,再来重新回顾。 @@ -207,7 +207,7 @@ const personality_analysis = blk: { 稍后,当我们讨论带标签的联合(tagged union)、错误联合(error unions)和可选类型(Optional)时,我们将看到控制流如何与它们联合使用。 -# [枚举]($section.id('enum')) +# [枚举]($heading.id('enum')) 枚举是带有标签的整数常量。它们的定义很像结构体: @@ -241,7 +241,7 @@ const Stage = enum { `switch` 的穷举性质使它能与枚举很好地搭配,因为它能确保你处理了所有可能的情况。不过在使用 `switch` 的 `else` 子句时要小心,因为它会匹配任何新添加的枚举值,而这可能不是我们想要的行为。 -# [带标签的联合 Tagged Union]($section.id('tagged-union')) +# [带标签的联合 Tagged Union]($heading.id('tagged-union')) 联合定义了一个值可以具有的一系列类型。例如,这个 `Number` 可以是整数、浮点数或 nan(非数字): @@ -320,7 +320,7 @@ const Timestamp = union(enum) { 这里 Zig 会根据带标签的联合,自动创建一个隐式枚举。 -# [可选类型 Optional]($section.id('optional')) +# [可选类型 Optional]($heading.id('optional')) 在类型前加上问号 `?`,任何值都可以声明为可选类型。可选类型既可以是 `null`,也可以是已定义类型的值: @@ -366,7 +366,7 @@ while (rows.next()) |row| { } ``` -# [未定义的值 Undefined]($section.id('undefined')) +# [未定义的值 Undefined]($heading.id('undefined')) 到目前为止,我们看到的每一个变量都被初始化为一个合理的值。但有时我们在声明变量时并不知道它的值。可选类型是一种选择,但并不总是合理的。在这种情况下,我们可以将变量设置为未定义,让其保持未初始化状态。 @@ -379,7 +379,7 @@ std.crypto.random.bytes(&pseudo_uuid); 上述代码仍然创建了一个 16 字节的数组,但它的每个元素都没有被赋值。 -# [错误 Errors]($section.id('errors')) +# [错误 Errors]($heading.id('errors')) Zig 中错误处理功能十分简单、实用。这一切都从错误集(error sets)开始,错误集的使用方式类似于枚举: diff --git a/content/learn/pointers.smd b/content/learn/pointers.smd index 3d1b7cd..5c74e6e 100644 --- a/content/learn/pointers.smd +++ b/content/learn/pointers.smd @@ -191,7 +191,7 @@ pub const User = struct { 现在,代码已按预期运行。虽然在函数参数和内存模型方面仍有许多微妙之处,但我们正在取得进展。现在也许是一个好时机来说明一下,除了特定的语法之外,这些都不是 Zig 所独有的。我们在这里探索的模型是最常见的,有些语言可能只是向开发者隐藏了很多细节,因此也就隐藏了灵活性。 -# [方法]($section.id('method')) +# [方法]($heading.id('method')) 一般来说,我们会把 `levelUp` 写成 `User`结构的一个方法: @@ -210,7 +210,7 @@ pub const User = struct { 我最初选择函数是因为它很明确,因此更容易学习。 -# [常量函数参数]($section.id('const-func-param')) +# [常量函数参数]($heading.id('const-func-param')) 我不止一次暗示过,在默认情况下,Zig 会传递一个值的副本(称为 "按值传递")。很快我们就会发现,实际情况要更微妙一些(提示:嵌套对象的复杂值怎么办?) @@ -222,7 +222,7 @@ pub const User = struct { > 也许你会想,即使与复制一个非常小的结构相比,通过引用传递怎么会更慢呢?我们接下来会更清楚地看到这一点,但要点是,当 `user` 是指针时,执行 `user.power` 会增加一点点开销。编译器必须权衡复制的代价和通过指针间接访问字段的代价。 -# [指向指针的指针]($section.id('pointer-to-pointer')) +# [指向指针的指针]($heading.id('pointer-to-pointer')) 我们之前查看了`main`函数中 `user` 的内存结构。现在我们改变了 `levelUp`,那么它的内存会是什么样的呢? @@ -258,7 +258,7 @@ fn levelUp(user: *User) void { 我们可以使用多级间接指针,但这并不是我们现在所需要的。本节的目的是说明指针并不特殊,它只是一个值,即一个地址和一种类型。 -# [嵌套指针]($section.id('nested-pointer')) +# [嵌套指针]($heading.id('nested-pointer')) 到目前为止,`User` 一直很简单,只包含两个整数。很容易就能想象出它的内存,而且当我们谈论『复制』 时,也不会有任何歧义。但是,如果 User 变得更加复杂并包含一个指针,会发生什么情况呢? @@ -371,7 +371,7 @@ pub const User = struct { 不同编程语言有不同的实现方式,但许多语言的工作方式与此完全相同(或非常接近)。虽然所有这些看似深奥,但却是日常编程的基础。好消息是,你可以通过简单的示例和片段来掌握这一点;它不会随着系统其他部分复杂性的增加而变得更加复杂。 -# [递归结构]($section.id('recursive-structs')) +# [递归结构]($heading.id('recursive-structs')) 有时你需要一个递归结构。在保留现有代码的基础上,我们为 `User` 添加一个可选的 `manager` 字段,类型为 `?User`。同时,我们将创建两个`User`,并将其中一个指定为另一个的管理者: diff --git a/content/learn/stack-memory.smd b/content/learn/stack-memory.smd index 9b4cfde..84599da 100644 --- a/content/learn/stack-memory.smd +++ b/content/learn/stack-memory.smd @@ -18,7 +18,7 @@ > 三块内存区域实际上没有真正的物理差别。操作系统和可执行文件创造了"内存区域"这个概念。 -# [栈帧]($section.id('stack-frame')) +# [栈帧]($heading.id('stack-frame')) 迄今为止,我们所见的所有数据都是常量,存储在二进制的全局数据部分或作为局部变量。局部表示该变量只在其声明的范围内有效。在 Zig 中,范围从花括号开始到结束。大多数变量的范围限定在一个函数内,包括函数参数,或一个控制流块,比如 if。但是,正如所见,你可以创建任意块,从而创建任意范围。 @@ -92,7 +92,7 @@ main: user -> ------------- (id: 1043368d0) 与全局数据一样,调用栈也由操作系统和可执行文件管理。程序启动时,以及此后启动的每个线程,都会创建一个调用栈(其大小通常可在操作系统中配置)。调用栈在程序的整个生命周期中都存在,如果是线程,则在线程的整个生命周期中都存在。程序或线程退出时,调用栈将被释放。我们的全局数据包含所有程序的全局数据,而调用栈只包含当前执行的函数层次的栈帧。这样做既能有效利用内存,又能简化堆栈帧的管理。 -# [悬空指针]($section.id('dangling-pointers')) +# [悬空指针]($heading.id('dangling-pointers')) 栈帧的简洁和高效令人惊叹。但它也很危险:当函数返回时,它的任何本地数据都将无法访问。这听起来似乎很合理,毕竟这是本地数据,但却会带来严重的问题。请看这段代码: diff --git a/content/learn/style-guide.smd b/content/learn/style-guide.smd index bacce52..4ac5b79 100644 --- a/content/learn/style-guide.smd +++ b/content/learn/style-guide.smd @@ -10,7 +10,7 @@ 本小节的主要内容是介绍 Zig 编译器强制遵守的 2 条规则,以及 Zig 标准库的命名惯例(naming convention)。 -# [未使用变量 Unused Variable]($section.id('unused-variable')) +# [未使用变量 Unused Variable]($heading.id('unused-variable')) Zig 编译器禁止`未使用变量`,例如以下代码会导致两处编译错误: @@ -55,7 +55,7 @@ fn add(a: i64, _: i64) i64 { 值得注意的是,在上述例子中,`std`也是一个未使用的符号,但是当前这种用法并不会导致任何编译错误。可能在未来,Zig 编译器也将此视为错误。 -# [变量覆盖 Variable Shadowing]($section.id('variable-shadowing')) +# [变量覆盖 Variable Shadowing]($heading.id('variable-shadowing')) Zig 不允许使用同名的变量。下面是一个读取 `socket` 的例子,这个例子包含了一个变量覆盖的编译错误: @@ -74,7 +74,7 @@ fn read(stream: std.net.Stream) ![]const u8 { 我认为,这个规范并不能使代码可读性提高。在这个场景下,应该是开发者,而不是编译器,更有资格选择更有可读性的命名方案。 -# [命名规范]($section.id('naming-convention')) +# [命名规范]($heading.id('naming-convention')) 除了遵守以上这些规则以外,开发者可以自由地选择他们喜欢的命名规范。但是,理解 Zig 自身的命名规范是有益的,因为大部分你需要打交道的代码,如 Zig 标准库,或者其他三方库,都采用了 Zig 的命名规范。 diff --git a/content/monthly/202207.smd b/content/monthly/202207.smd index eaf2e29..2587e78 100644 --- a/content/monthly/202207.smd +++ b/content/monthly/202207.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) - [Zig 初体验 - Keep Coding](https://liujiacai.net/blog/2022/07/16/zig-intro/) @@ -28,7 +28,7 @@ before starting with Zig? : Zig](https://www.reddit.com/r/Zig/comments/w63x6r/is_it_necessary_to_know_c_or_another_systems/) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) - [Release bun v0.1.5 · oven-sh/bun](https://github.com/oven-sh/bun/releases/tag/bun-v0.1.5) diff --git a/content/monthly/202208.smd b/content/monthly/202208.smd index 841eaf3..82604f1 100644 --- a/content/monthly/202208.smd +++ b/content/monthly/202208.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) - [Growing a {{mustache}} with Zig](https://zig.news/batiati/growing-a-mustache-with-zig-di4) @@ -29,7 +29,7 @@ devlog](https://devlog.hexops.com/2022/packed-structs-in-zig/)。使用 Zig 的 `packet struct` 实现 bit set 功能 -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) - [Virtual tables by vrischmann · Pull Request \#100 · vrischmann/zig-sqlite](https://github.com/vrischmann/zig-sqlite/pull/100)。zig-sqlite diff --git a/content/monthly/202209.smd b/content/monthly/202209.smd index 1795eaf..8f87cc2 100644 --- a/content/monthly/202209.smd +++ b/content/monthly/202209.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [Zig VS Rust 火花]($section.id('Zig VS Rust 火花')) +# [Zig VS Rust 火花]($heading.id('zig-vs-rust-spark')) 在 9/10 号左右,在 Twitter 上牵起了一小波关于 Zig VS Rust 的小火花,以至于最后 Zig 创始人 Andrew Kelley @@ -16,7 +16,7 @@ Let us exist。这里稍微整理下这件事情的过程: 本次事件主要 - Rust 核心贡献者: Patrick Walton - Zig 社区 VP: Loris Cro -## [时间线]($section.id('时间线')) +## [时间线]($heading.id('timeline')) - 8/26 号,一篇关于 wasm 2 Game Jam 的[分析报告](https://wasm4.org/blog/jam-2-results/)中,使用 Zig @@ -57,7 +57,7 @@ Let us exist。这里稍微整理下这件事情的过程: 本次事件主要 这里[提到的](https://github.com/tigerbeetledb/tigerbeetle/blob/main/docs/DESIGN.md)。而且即便内存安全,也可能发生 OOM -## [总结]($section.id('总结')) +## [总结]($heading.id('summary')) 上面的链接比较多,这里稍微总结下这次争论的问题: @@ -69,7 +69,7 @@ Zig,笔者觉得大概率就是本次争论的观点,Rust 过于复杂,导致程序员不仅仅要考虑业务行为,还需要按照 Rust 的风格来编程,这加剧了程序出错的可能性。 -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) - [How (memory) safe is zig?](https://www.scattered-thoughts.net/writing/how-safe-is-zig/) @@ -102,7 +102,7 @@ Zig,笔者觉得大概率就是本次争论的观点,Rust - [Zig ⚡ Improving the User Experience for Unused Variables](https://vimeo.com/748218307) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) - [Zig 开发常用类库](https://github.com/zigcc/forum/discussions/28),如果读者的 diff --git a/content/monthly/202210.smd b/content/monthly/202210.smd index 925b42c..227258f 100644 --- a/content/monthly/202210.smd +++ b/content/monthly/202210.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) - [Zig Is Self-Hosted Now, What’s Next? | Loris Cro’s Blog](https://kristoff.it/blog/zig-self-hosted-now-what/) | 0.10 @@ -39,7 +39,7 @@ - \[音频\] [005. 与 LemonHX 畅聊新一代编程语言 Zig – RustTalk](https://rusttalk.github.io/podcast/005/) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) - [Himujjal/zig-json5](https://github.com/Himujjal/zig-json5): A JSON5 parser/stringifier for Zig resembling the std.json API diff --git a/content/monthly/202211.smd b/content/monthly/202211.smd index 5b1435c..87d90ad 100644 --- a/content/monthly/202211.smd +++ b/content/monthly/202211.smd @@ -17,13 +17,13 @@ compiler,也称为『自举』,即可以用 Zig 来写 Zig 赶紧升级吧,少年! -# [zigcc 中文社区微信群]($section.id('zigcc 中文社区微信群')) +# [zigcc 中文社区微信群]($heading.id('zigcc-chinese-community-wechat-group')) 欢迎喜欢 Zig 的小伙伴加入! {{\< figure src=“https://github.com/zigcc/.github/raw/main/weixin.jpg” width=“200” title=“ZigCC 微信群二维码” \>}} -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) - [Wasmer 3.0 使用 Zig 进行跨平台编译 · Discussion \#35 · zigcc/forum](https://github.com/zigcc/forum/discussions/35) @@ -98,7 +98,7 @@ const Animal = union(enum){ [spoon](https://sr.ht/~leon_plickat/zig-spoon/) 这个库开发了一个帮助自己进行图片打标的 UI 工具, -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) - [Zig 程序设计语言中文手册](https://sxwangzhiwen.github.io/zigcndoc/zigcndoc.html) diff --git a/content/monthly/202212.smd b/content/monthly/202212.smd index e571d32..2720888 100644 --- a/content/monthly/202212.smd +++ b/content/monthly/202212.smd @@ -6,11 +6,11 @@ .draft = false, --- -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) - [Goodbye to the C++ Implementation of Zig ⚡ Zig Programming Language](https://ziglang.org/news/goodbye-cpp/) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) # [Zig 语言更新](https://github.com/ziglang/zig/pulls?page=1&q=+is%3Aclosed+is%3Apr+closed%3A2022-12-01..2023-01-01) diff --git a/content/monthly/202301.smd b/content/monthly/202301.smd index 7fb1cf9..0e410ea 100644 --- a/content/monthly/202301.smd +++ b/content/monthly/202301.smd @@ -59,7 +59,7 @@ pub fn build(b: *std.build.Builder) void { [15.0.7](http://releases.llvm.org/15.0.7/docs/ReleaseNotes.html) - 是 0.10.x 的最后一个 release 版本 -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) [Code study: interface idioms/patterns in zig standard libraries](https://zig.news/yglcode/code-study-interface-idiomspatterns-in-zig-standard-libraries-4lkj) @@ -77,7 +77,7 @@ Beetle](https://datastackshow.com/podcast/why-accounting-needs-its-own-database- Zig](https://0110.be/posts/Crossplatform_JNI_builds_with_Zig) 又一个使用 Zig 作为交叉编译的例子 -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) [Introducing ⚡zap⚡ - blazingly fast backends in zig](https://zig.news/renerocksai/introducing-zap-blazingly-fast-backends-in-zig-3jhh) diff --git a/content/monthly/202302.smd b/content/monthly/202302.smd index 12527f1..c46e723 100644 --- a/content/monthly/202302.smd +++ b/content/monthly/202302.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [包管理器进展]($section.id('包管理器进展')) +# [包管理器进展]($heading.id('package-manager-progress')) 包管理器自 [\#14265](https://github.com/ziglang/zig/pull/14265) 合并后一直在不断推进,以下两个是最主要的改变: @@ -61,7 +61,7 @@ 也欢迎大家给自己熟悉的 C/C++ 项目提 PR 让其支持 zig build,让构建不再那么痛苦。 -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) - [How a Zig IDE Could Work](https://matklad.github.io/2023/02/10/how-a-zig-ide-could-work.html) @@ -105,7 +105,7 @@ build,让构建不再那么痛苦。 - [Smoking Hot Binary Search In Zig](https://blog.deckc.hair/2023-02-22-smoking-hot-binary-search-in-zig.html) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) - [Writing high-performance clients for TigerBeetle](https://tigerbeetle.com/blog/2023-02-21-writing-high-performance-clients-for-tigerbeetle/) diff --git a/content/monthly/202303.smd b/content/monthly/202303.smd index 05a3771..f9f687b 100644 --- a/content/monthly/202303.smd +++ b/content/monthly/202303.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) [Creating arbitrary error values by using error.Something syntax](https://www.reddit.com/r/zig/comments/11wmoky) @@ -61,7 +61,7 @@ Nix](https://flyx.org/cross-packaging/) [Zig And Rust](https://matklad.github.io/2023/03/26/zig-and-rust.html) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) [macovedj/doink](https://github.com/macovedj/doink) Making WebAssembly Components with Zig @@ -98,7 +98,7 @@ bug,一个影响比较大的是 test 的构建方式不一样了。 现在 `addTest` 和 `addExecutable` 一样,输出都是 `CompileStep` ,它默认不会执行,需要调用 `run()` 拿到 run step 才可以。 -# [Zig 构建系统介绍]($section.id('zig-build-system-intro')) +# [Zig 构建系统介绍]($heading.id('zig-build-system-intro')) 构建系统其实是 Zig 生态中比较重要的一环,和 Rust 不同,Zig 即是一门编程语言,也是一系列工具链,比如编译 Zig 时,没有 zigc diff --git a/content/monthly/202304.smd b/content/monthly/202304.smd index dee548b..9d81e3b 100644 --- a/content/monthly/202304.smd +++ b/content/monthly/202304.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [重大事件]($section.id('重大事件')) +# [重大事件]($heading.id('major-events')) 在 2023 四月份的 [Tiobe](https://www.tiobe.com/tiobe-index/) 指数上,Zig [排名 @@ -29,7 +29,7 @@ Loris 发推表示这个数字对 Zig \> -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) [When should I use an UNTAGGED Union?](https://zig.news/kristoff/when-should-i-use-an-untagged-union-56ek) @@ -78,7 +78,7 @@ Systems Distributed 23 视频,[B 站链接](https://www.bilibili.com/video/BV1gP41117zY/),作者博客:[Scattered Thoughts](https://www.scattered-thoughts.net/) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) [Coming Soon to a Zig Near You: HTTP Client](https://zig.news/nameless/coming-soon-to-a-zig-near-you-http-client-5b81) diff --git a/content/monthly/202305.smd b/content/monthly/202305.smd index 7654cd0..d6e6576 100644 --- a/content/monthly/202305.smd +++ b/content/monthly/202305.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [重大事件]($section.id('重大事件')) +# [重大事件]($heading.id('major-events')) 这个月主要的事情就是 HTTP server 在标准库的增加了,具体可参考: @@ -15,7 +15,7 @@ - [http server in the standard library · Issue \#910](https://github.com/ziglang/zig/issues/910) -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) [Integrating Zig and SwiftUI](https://mitchellh.com/writing/zig-and-swiftui) @@ -67,7 +67,7 @@ System](https://www.priver.dev/blog/zig/initial-commit-build-system/) [Writing DNS resolver in Zig](https://e-aakash.github.io/update/2023/06/04/resolv-dns-resolver-in-zig.html) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) [Zig by Example](https://zigbyexample.github.io/) 非常好的学习资料。Learn How to use Zig’s Standard Library, by small diff --git a/content/monthly/202306.smd b/content/monthly/202306.smd index cb8a897..d978407 100644 --- a/content/monthly/202306.smd +++ b/content/monthly/202306.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [重大事件]($section.id('重大事件')) +# [重大事件]($heading.id('major-events')) 一个是这个:[The Zig subreddit has closed](https://ziggit.dev/t/the-zig-subreddit-has-closed/679),现在 @@ -91,7 +91,7 @@ Zig 的各种 backend 进展,能不能给 fix 几个 regression?! 最后,即便 Zig 这个项目夭折了,我相信通过这个学习的过程也有助于提高我们对系统编程、编译器的理解,不是吗? -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) [A Note About Zig Books for the Zig Community](https://kristoff.it/blog/note-about-zig-books/) @@ -147,14 +147,14 @@ segfaults](https://www.openmymind.net/Zig-Danling-Pointers/) 讨论](https://news.ycombinator.com/item?id=36149462) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) [pondzdev/duckdb](https://github.com/pondzdev/duckdb-proxy/) 一个将 DuckDB 数据库通过 HTTP API 暴露出来的代理,主要是利用了 [DuckDB C API](https://duckdb.org/docs/api/c/api.html),示例: ``` bash -# [open the database in readonly (DB must exist in this case)]($section.id('open the database in readonly (DB must exist in this case)')) +# open the database in readonly (DB must exist in this case) $ ./duckdb-proxy --readonly db/mydatabase.duckdb $ curl http://localhost:8012/api/1/exec \ diff --git a/content/monthly/202307.smd b/content/monthly/202307.smd index 39f898e..78ccf39 100644 --- a/content/monthly/202307.smd +++ b/content/monthly/202307.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [重大事件]($section.id('重大事件')) +# [重大事件]($heading.id('major-events')) Andrewk 在最新的文章 [The Upcoming Release Postponed Two More Weeks and Lacks Async Functions](https://ziglang.org/news/0.11.0-postponed-again/) @@ -24,7 +24,7 @@ Team](https://ziglang.org/news/welcome-jacob-young/),Core Team 恭喜 Core Team,又添一虎将! -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) [Copy Hunting | TigerBeetle](https://tigerbeetle.com/blog/2023-07-26-copy-hunting/) 比较有意思的文章,通过分析 LLVM 的 IR,来避免程序中不必要的 memcpy @@ -129,7 +129,7 @@ TigerBeetle 的新花样,把数据库搬到了 Web 上。[HN 比较有意思的文章,作者比较了 Clang、Zig 对相同逻辑代码生产的 LLVM IR,Zig 生成的 IR 更加精简 -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) [Zig helped us move data to the Edge. Here are our impressions](https://blog.turso.tech/zig-helped-us-move-data-to-the-edge-here-are-our-impressions-67d3a9c45af4) [Turso](https://turso.tech/) diff --git a/content/monthly/202308.smd b/content/monthly/202308.smd index c8567ff..d518254 100644 --- a/content/monthly/202308.smd +++ b/content/monthly/202308.smd @@ -11,7 +11,7 @@ 0.11 终于在 8 月 4 号释出了,下面来看看它的一些重要改进吧。[HN 讨论](https://news.ycombinator.com/item?id=36995735) -# [Peer Type Resolution Improvements]($section.id('Peer Type Resolution Improvements')) +# [Peer Type Resolution Improvements]($heading.id('peer-type-resolution-improvements')) 对等类型解析算法得到改进,下面是一些在 0.10 中不能解析,但在 0.11 中可以解析的例子: @@ -29,7 +29,7 @@ 而且现在使用 `@intCast` 这类 builtin 都只接受一个参数,目前类型根据上下文自动推断出来。 -# [Multi-Object For Loops]($section.id('Multi-Object For Loops')) +# [Multi-Object For Loops]($heading.id('multi-object-for-loops')) 可以同时对多个对象进行遍历: @@ -45,7 +45,7 @@ for (input, 0..) |x, i| { } ``` -# [@min and @max]($section.id('@min and @max')) +# [@min and @max]($heading.id('min-and-max')) 主要有两个改动: @@ -65,7 +65,7 @@ test "@min/@max refines result type" { } ``` -# [@inComptime]($section.id('@inComptime')) +# [@inComptime]($heading.id('incomptime')) 新加的 builtin,用于判断执行是否在 comptime 环境下执行: @@ -95,12 +95,12 @@ test "@inComptime" { } ``` -# [类型转化相关 builtin 的重命名]($section.id('类型转化相关 builtin 的重命名')) +# [类型转化相关 builtin 的重命名]($heading.id('renaming-of-builtin-related-to-type-conversion')) 之前 `@xToY` 形式的 builtin 现在已经改成了 `@yFromX` ,这样的主要好处是便于阅读(从右向左),这是[草案](https://github.com/ziglang/zig/issues/6128)。 -# [Tuple 类型声明]($section.id('Tuple 类型声明')) +# [Tuple 类型声明]($heading.id('tuple-type-declaration')) 现在可以直接用无 field 名字的 struct 来声明 tuple 类型: @@ -133,7 +133,7 @@ test "tuple declarations" { + const testcases = [_]struct { []const u8, []const u8, bool }{ ``` -# [排序]($section.id('排序')) +# [排序]($heading.id('sort')) 现在排序算法分布两类: @@ -143,7 +143,7 @@ test "tuple declarations" { 与堆排的快速最差情况相结合,同时在具有特定模式的输入上达到线性时间。 -# [Stack Unwinding]($section.id('Stack Unwinding')) +# [Stack Unwinding]($heading.id('stack-unwinding')) Zig 之前依赖 [frame pointer](https://en.wikipedia.org/wiki/Call_stack#Stack_and_frame_pointers) @@ -154,20 +154,20 @@ pointer](https://en.wikipedia.org/wiki/Call_stack#Stack_and_frame_pointers) DWARF unwind tables 和 MachO compact unwind information 来会恢复堆栈,详见:[\#15823](https://github.com/ziglang/zig/pull/15823)。 -# [包管理]($section.id('包管理')) +# [包管理]($heading.id('package-management')) 0.11 首次正式引入了包管理器,具体解释可以参考:[Zig Build System](https://en.liujiacai.net/2023/04/13/zig-build-system/),而且很重要一点,step 之间可以[并发执行](https://ziglang.org/download/0.11.0/release-notes.html#Steps-Run-In-Parallel), -# [Bootstrapping]($section.id('Bootstrapping')) +# [Bootstrapping]($heading.id('bootstrapping')) C++ 实现的 Zig 编译器已经被彻底移除,这意味着 `-fstage1` 不再生效,Zig 现在只需要一个 2.4M 的 WebAssembly 文件和一个 C 编辑器即可,工作细节可以参考:[Goodbye to the C++ Implementation of Zig](https://ziglang.org/news/goodbye-cpp/)。 -# [代码生成]($section.id('代码生成')) +# [代码生成]($heading.id('code-generation')) 虽然 Zig 编译器现在还是主要使用 LLVM 来进行代码生成,但在这次发布中,其他几个后端也有了非常大的进步: @@ -182,7 +182,7 @@ Zig](https://ziglang.org/news/goodbye-cpp/)。 后端专注于为 OpenCL 内核生成代码,不过未来可能也会支持兼容 Vulkan 的着色器。 -# [增量编译]($section.id('增量编译')) +# [增量编译]($heading.id('incremental-compilation')) 虽然这仍是一个高度 WIP 的功能,但这一版本周期中的许多改进为编译器的增量编译功能铺平了道路。其中最重要的是 @@ -190,7 +190,7 @@ Zig](https://ziglang.org/news/goodbye-cpp/)。 用户大多看不到这一改动,但它为编译器带来了许多好处,其中之一就是我们现在更接近增量编译了。增量编译将是 0.12.0 发布周期的重点。 -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) [Error Handling In Zig](https://www.aolium.com/karlseguin/4013ac14-2457-479b-e59b-e603c04673c8) 又一篇讨论错误处理的文章 @@ -222,7 +222,7 @@ Andrew 的最新文章,远离社交平台! [Taking off with Zig: Putting the Z in Benchmark — Double Trouble](https://double-trouble.dev/post/zbench/) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) - [Mach v0.2 released](https://devlog.hexops.com/2023/mach-v0.2-released/) diff --git a/content/monthly/202309.smd b/content/monthly/202309.smd index 52332bb..09ea3f3 100644 --- a/content/monthly/202309.smd +++ b/content/monthly/202309.smd @@ -6,9 +6,9 @@ .draft = false, --- -# [重大事件]($section.id('重大事件')) +# [重大事件]($heading.id('major-events')) -# [Bounties Damage Open Source Projects]($section.id('bounties-damage-open-source-projects')) +# [Bounties Damage Open Source Projects](https://ziglang.org/news/bounties-damage-open-source-projects) 在 2023-09-11 号,Wasmerio CEO 创建了 [Support WASIX · Issue \#17115](https://github.com/ziglang/zig/issues/17115),表示想赞助 Zig @@ -38,7 +38,7 @@ Bun 并不简单的是个 Node.js 替代品,而是大而全的工具链: - Testing libraries,内置 test runner,支持快照测试、模拟和代码覆盖,可以替代:jest、ts-test -# [观点/教程]($section.id('观点/教程')) +# [观点/教程]($heading.id('viewstutorials')) [Kiesel Devlog \#1: Now passing 25% of test262](https://linus.dev/posts/kiesel-devlog-1/) 另一个 devlog,作者写了一个 JS engine 用来学习 Zig,该作者是 SerenityOS @@ -85,7 +85,7 @@ Mitchell Hashimoto 在这篇文章里分享了开发终端 Ghostty 时用的 Zig [SoA 内存布局]($image.siteAsset('images/soa-layout.webp')) -# [项目/工具]($section.id('项目/工具')) +# [项目/工具]($heading.id('projectstools')) [ZigBrains](https://plugins.jetbrains.com/plugin/22456-zigbrains) A multifunctional Zig Programming Language plugin for the IDEA platform. diff --git a/content/monthly/202310.smd b/content/monthly/202310.smd index 0bafef5..b21b34c 100644 --- a/content/monthly/202310.smd +++ b/content/monthly/202310.smd @@ -6,9 +6,9 @@ .draft = false, --- -# [重大事件]($section.id('major-events')) +# [重大事件]($heading.id('major-events')) -# [观点/教程]($section.id('opinion-tutorial')) +# [观点/教程]($heading.id('opinion-tutorial')) [Notes From the Field: Learning Zig](https://registerspill.thorstenball.com/p/notes-from-the-field-learning-zig) Zig 初学者的使用经验分享 @@ -96,7 +96,7 @@ pub fn main() !void { 由于 Zig 还在快速开发迭代中,因此项目很有可能出现新版本 Zig 无法编译的情况,这篇文章介绍了一些管理多个 Zig 版本的方式。 -# [项目/工具]($section.id('projects-tools')) +# [项目/工具]($heading.id('projects-tools')) [zigcli](https://zigcli.liujiacai.net/) a toolkit for building command lines programs in Zig diff --git a/content/monthly/202311.smd b/content/monthly/202311.smd index 78d02ff..cbf7370 100644 --- a/content/monthly/202311.smd +++ b/content/monthly/202311.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [重大事件]($section.id('major-events')) +# [重大事件]($heading.id('major-events')) 本月讨论比较多的就是 [Zig May Pass Anything By Reference](https://www.1a-insec.net/blog/25-zig-reference-semantics/) @@ -58,7 +58,7 @@ precisely』,目前来看确实是这样的,而且 core team - [Design flaw: Swapping struct fields yields unexpected value \#12064](https://github.com/ziglang/zig/issues/12064) -# [观点/教程]($section.id('opinion-tutorial')) +# [观点/教程]($heading.id('opinion-tutorial')) [Zig's std.json.Parsed(T)](https://www.openmymind.net/Zigs-std-json-Parsed/) 老朋友 openmymind 的文章,这篇文章主要讲述了使用 Zig 中的 json @@ -147,7 +147,7 @@ Fields - 一个包的兼容性,应该由文档来解释 - 增加私有字段,会增加语言的复杂度,而且这种复杂性本身是完全可以避免的 -# [项目/工具]($section.id('projects-tools')) +# [项目/工具]($heading.id('projects-tools')) [zig build explained – building C/C++ projects](https://zig.news/xq/zig-build-explained-part-2-1850) 经典文章回顾,如何使用 Zig 构建系统编译 C/C++ 项目 diff --git a/content/monthly/202402.smd b/content/monthly/202402.smd index 2e05929..1e0b2ad 100644 --- a/content/monthly/202402.smd +++ b/content/monthly/202402.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [重大事件]($section.id('major-events')) +# [重大事件]($heading.id('major-events')) Andrew 最近在 zigshow 节目中介绍了 Zig 2024 年的规划,主要有以下几点: @@ -32,7 +32,7 @@ Andrew 最近在 zigshow 节目中介绍了 Zig 2024 年的规划,主要有以 \#91](https://github.com/orgs/zigcc/discussions/91) B 站搬运地址:https://www.bilibili.com/video/BV1UC4y167w9/ -# [观点/教程]($section.id('opinion-tutorial')) +# [观点/教程]($heading.id('opinion-tutorial')) [Fast-growing Zig tops Stack Overflow survey for highest-paid programming language](https://www.infoworld.com/article/3713082/fast-growing-zig-tops-stack-overflow-survey-for-highest-paid-programming-language.html) 估计是 Bun 带动的贫富差距?! @@ -67,7 +67,7 @@ Syndica 公司的 Sig,这是一款用 Zig 编写的、专注于 RPS 的 Solana - [Senior Software Engineer (Zig/C/Rust) @ Syndica](https://jobs.ashbyhq.com/syndica/15ab4e32-0f32-41a0-b8b0-16b6518158e9) -# [项目/工具]($section.id('projects-tools')) +# [项目/工具]($heading.id('projects-tools')) [semickolon/kirei](https://github.com/semickolon/kirei) 🌸 The prettiest keyboard software diff --git a/content/monthly/202403.smd b/content/monthly/202403.smd index 4049567..fcd5b25 100644 --- a/content/monthly/202403.smd +++ b/content/monthly/202403.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [重大事件]($section.id('major-events')) +# [重大事件]($heading.id('major-events')) > @@ -39,7 +39,7 @@ Kelley](https://rustacean-station.org/episode/andrew-kelley/) 也会紧密关注发布动态,有消息第一时间分享给大家。耐不住寂寞的朋友,可以先去刷刷 Zig 的 discord。 -# [观点/教程]($section.id('opinion-tutorial')) +# [观点/教程]($heading.id('opinion-tutorial')) [Redesign How Autodoc Works](https://github.com/ziglang/zig/pull/19208) Andrew 在这个 PR 里重构了现有的文档系统 @@ -235,7 +235,7 @@ const port = port: { [Using Zig with WebAssembly](https://blog.mjgrzymek.com/blog/zigwasm) 如何将 Zig 编译成 wasm,并传递复杂的参数。 -# [项目/工具]($section.id('projects-tools')) +# [项目/工具]($heading.id('projects-tools')) [xataio/pgzx](https://github.com/xataio/pgzx) Create PostgreSQL extensions using Zig. 一个例子: diff --git a/content/monthly/202404.smd b/content/monthly/202404.smd index 098f207..c92c564 100644 --- a/content/monthly/202404.smd +++ b/content/monthly/202404.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [重大事件]($section.id('major-events')) +# [重大事件]($heading.id('major-events')) 千呼万唤的 0.12.0 版本终于 2024-04-20 正式释出了!这次版本历时 8 个月,有 268 位贡献者,一共进行了 3688 次提交!社区内的一些讨论:[Hacker @@ -37,7 +37,7 @@ CMake、Makefile、Vcpkg、Git submodule 等工具,所有的依赖使用 zon 期待一年后 Zig 的生态! -# [观点/教程]($section.id('opinion-tutorial')) +# [观点/教程]($heading.id('opinion-tutorial')) [Zig 中任意精度整数用途与实现](https://github.com/zigcc/forum/issues/112) 由于 CPU @@ -104,7 +104,7 @@ Vector,到最后利用 bit 的特点,来逐步优化,并用 godbolt [Documentation takes another step backwards : r/Zig](https://www.reddit.com/r/Zig/comments/1cc1x2v/documentation_takes_another_step_backwards/) 一个 Reddit 用户对文档的抱怨 -# [项目/工具]($section.id('projects-tools')) +# [项目/工具]($heading.id('projects-tools')) [rofrol/zig-companies](https://github.com/rofrol/zig-companies) A list of companies using Zig in production. diff --git a/content/monthly/202405.smd b/content/monthly/202405.smd index a64929a..99df486 100644 --- a/content/monthly/202405.smd +++ b/content/monthly/202405.smd @@ -6,14 +6,14 @@ .draft = false, --- -# [观点/教程]($section.id('opinion-tutorial')) +# [观点/教程]($heading.id('opinion-tutorial')) # [Thoughts on Zig](https://arne.me/blog/thoughts-on-zig) 又一篇 Zig 初学者的使用体验文档,如果你也在犹豫要不要学 Zig,这是个不错的经验参考。 -# [I'm sold on Zig's simplicity : r/Zig]($section.id('im-sold-on-zigs-simplicity-r-zig')) +# [I'm sold on Zig's simplicity : r/Zig]($heading.id('im-sold-on-zigs-simplicity-r-zig')) 一个具有资深经验开发者,在这里描述了自己选择业余项目语言的经历: @@ -32,7 +32,7 @@ Zig,这是个不错的经验参考。 - 唯一缺失的就是『接口』,但这一点并不是很关键,就像在 C里也没有,但是 C 可以做任何事 -# [Zig's New CLI Progress Bar Explained]($section.id('zigs-new-cli-progress-bar-explained')) +# [Zig's New CLI Progress Bar Explained]($heading.id('zigs-new-cli-progress-bar-explained')) Andrew 的一篇文章,讲述了在最新版的 Zig 中,对进度条的改进实现,现在的进度展示更加友好。 @@ -81,7 +81,7 @@ Openmymind } ``` -# [项目/工具]($section.id('projects-tools')) +# [项目/工具]($heading.id('projects-tools')) [zigar](https://github.com/chung-leong/zigar) Enable the use of Zig code in JavaScript project。它可以让你直接在 JS diff --git a/content/monthly/202406.smd b/content/monthly/202406.smd index 0447060..b3ead4a 100644 --- a/content/monthly/202406.smd +++ b/content/monthly/202406.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [重大事件]($section.id('major-events')) +# [重大事件]($heading.id('major-events')) 2024-06-07,0.13.0 发布,历时不足 2 个月,有 73 位贡献者,一共进行了 415 次提交! 这是一个相对较短的发布周期,主要原因是工具链升级,例如升级到 @@ -29,7 +29,7 @@ const map = std.StaticStringMap(T).initComptime(kvs_list); - 启用增量编译以实现快速重建。 - 将并发引入语义分析,进一步提高编译速度。 -# [观点/教程]($section.id('opinion-tutorial')) +# [观点/教程]($heading.id('opinion-tutorial')) # [Leveraging Zig's Allocators](https://www.openmymind.net/Leveraging-Zigs-Allocators/) @@ -136,7 +136,7 @@ Rust。 重写的项目运行在多个平台上(Web、移动端、VR Zig resources](https://ludwigabap.bearblog.dev/2024-collection-of-zig-resources/) -# [Why I am not yet ready to switch to Zig from Rust]($section.id('why-i-am-not-yet-ready-to-switch-to-zig-from-rust')) +# [Why I am not yet ready to switch to Zig from Rust](https://turso.tech/blog/why-i-am-not-yet-ready-to-switch-to-zig-from-rust) Turso CTO 的一篇文章,他本身是个资深 C 程序员,而且也比较喜欢 C,但 C 不是一门安全的语言,因此通过 Rust,作者可以避免 写出 SIGSEGVS @@ -157,7 +157,7 @@ Turso CTO 的一篇文章,他本身是个资深 C 程序员,而且也比较 其他社区的一些讨论:[Lobsters](https://lobste.rs/s/0mnhdx)、[Hacker News](https://news.ycombinator.com/item?id=40681862) -# [项目/工具]($section.id('projects-tools')) +# [项目/工具]($heading.id('projects-tools')) [malcolmstill/zware](https://github.com/malcolmstill/zware) Zig WebAssembly Runtime Engine diff --git a/content/monthly/202407.smd b/content/monthly/202407.smd index 63234c2..8366be8 100644 --- a/content/monthly/202407.smd +++ b/content/monthly/202407.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [重大事件]($section.id('major-events')) +# [重大事件]($heading.id('major-events')) 在[这篇文章](https://leaddev.com/tech/why-zig-one-hottest-programming-languages-learn)里,作者引用 Stackoverflow 2024 @@ -34,7 +34,7 @@ Zig 开发者的一些观点: 但与此同时,这些语言的工具链却非常糟糕。 Zig 允许用户涉猎这些核心编程语言,但可以使用更好的工具链,兼容各种语言和更丰富的功能。 -# [观点/教程]($section.id('opinion-tutorial')) +# [观点/教程]($heading.id('opinion-tutorial')) # [Improving Your Zig Language Server Experience](https://kristoff.it/blog/improving-your-zls-experience/) @@ -82,7 +82,7 @@ zls --config-path zls.json 这样不同的项目就可以用不同的检查步骤了。 -# [Systems Distributed '24]($section.id('systems-distributed-24')) +# [Systems Distributed '24](https://guergabo.substack.com/p/systems-distributed-24) 作者对这次会议的一个回顾总结,议题主要有如下几个方向: @@ -167,7 +167,7 @@ fn get_window_messages() [65536][:0]const u8 { - 在 JavaScript 允许您内联表达式的某些情况下,您可能需要分解出一个变量来澄清生存期。看看[这个问题](https://github.com/ziglang/zig/issues/12414)。 -# [项目/工具]($section.id('projects-tools')) +# [项目/工具]($heading.id('projects-tools')) [18alantom/fex](https://github.com/18alantom/fex) A command-line file explorer prioritizing quick navigation. diff --git a/content/monthly/202410.smd b/content/monthly/202410.smd index d0b4b3b..0a099fb 100644 --- a/content/monthly/202410.smd +++ b/content/monthly/202410.smd @@ -6,9 +6,9 @@ .draft = false, --- -# [重大事件]($section.id('major-events')) +# [重大事件]($heading.id('major-events')) -# [向 Zig 软件基金会认捐 30 万美元]($section.id('donation-zsf-300k')) +# [向 Zig 软件基金会认捐 30 万美元]($heading.id('donation-zsf-300k')) Mitchell 在其最新的博客上宣布:我和我的妻子向 Zig 软件基金会 (ZSF) 捐赠了 300,000 美元。 @@ -25,7 +25,7 @@ Mitchell 在其最新的博客上宣布:我和我的妻子向 Zig 软件基金 作为其中的一部分,我们希望支持那些我们认为可以带来变革和影响的独立软件项目,这既是回馈给我如此之多的社区的一种方式,更重要的是,这也是彰显和鼓励为热爱而构建的文化的一种方式。 Zig 就是这样一个项目。 -# [观点/教程]($section.id('opinion-tutorial')) +# [观点/教程]($heading.id('opinion-tutorial')) # [Zig is everything I want C to be](https://mrcat.au/blog/zig_is_cool/) @@ -74,15 +74,15 @@ Zig 就是这样一个项目。 - - -# [The Zig Website Has Been Re-engineered]($section.id('the-zig-website-has-been-re-engineered')) +# [The Zig Website Has Been Re-engineered](https://ziglang.org/news/website-zine) Zig 官网已经用 [Zine](https://zine-ssg.io/) 重写! # [Rust vs. Zig in Reality: A (Somewhat) Friendly Debate](https://thenewstack.io/rust-vs-zig-in-reality-a-somewhat-friendly-debate/) -# [Why I rewrote my Rust keyboard firmware in Zig: consistency, mastery, and fun]($section.id('why-i-rewrote-my-rust-keyboard-firmware-in-zig-consistency-mastery-and-fun')) +# [Why I rewrote my Rust keyboard firmware in Zig: consistency, mastery, and fun](https://teckrevie.blogspot.com/2024/09/why-zig-programmers-are-cashing-in-deep.html) -# [视频]($section.id('videos')) +# [视频]($heading.id('videos')) ## [I made an operating system that self replicates doom on a network "from scratch"](https://www.youtube.com/watch?v=xOySJpQlmv4&feature=youtu.be) @@ -90,7 +90,7 @@ Zig 官网已经用 [Zine](https://zine-ssg.io/) 重写! ## [Let's explore Vulkan API with Zig programming language from scratch](https://www.youtube.com/live/Kf7BIPUUfsc?t=764) -# [项目/工具]($section.id('projects-tools')) +# [项目/工具]($heading.id('projects-tools')) [laohanlinux/boltdb-zig](https://github.com/laohanlinux/boltdb-zig) a zig implement kv database diff --git a/content/monthly/202411.smd b/content/monthly/202411.smd index 55081fc..7931766 100644 --- a/content/monthly/202411.smd +++ b/content/monthly/202411.smd @@ -6,7 +6,7 @@ .draft = false, --- -# [观点/教程]($section.id('opinion-tutorial')) +# [观点/教程]($heading.id('opinion-tutorial')) # [Why am I writing a JavaScript toolchain in Zig?](https://injuly.in/blog/announcing-jam/index.html) @@ -48,7 +48,7 @@ esquery 的问题在于执行效率,通过借助于 zig 的 comptime 来在编译器翻译 esquery 就避免了运行时开销。 -# [Advent of Code in Zig | Loris Cro's Blog]($section.id('advent-of-code-in-zig-loris-cros-blog')) +# [Advent of Code in Zig | Loris Cro's Blog](https://kristoff.it/blog/advent-of-code-zig/) 一年一度的 AoC 又到了,这篇文章里给出了一些使用的技巧来帮助大家用 Zig 来解决 AoC 问题。 @@ -99,7 +99,7 @@ > 的某些功能甚至能帮助您取得比其他语言更快的进展,但它最终还是针对软件工程进行了优化,而这并不是您在 > AoC 中要做的事情。 -# [Zig and Emulators]($section.id('zig-and-emulators')) +# [Zig and Emulators](https://floooh.github.io/2024/08/24/zig-and-emulators.html) # [Zig Reproduced Without Binaries](https://jakstys.lt/2024/zig-reproduced-without-binaries/) @@ -130,7 +130,7 @@ Zig,利用 LLVM 后端,以 wasm32-wasi 为目标生成的二进制文件。 - [Why is the bootstrapping process so complicated? : r/Zig](https://www.reddit.com/r/Zig/comments/142gwls/why_is_the_bootstrapping_process_so_complicated/) -# [项目/工具]($section.id('projects-tools')) +# [项目/工具]($heading.id('projects-tools')) [Builds (Zig) - GoReleaser](https://goreleaser.com/customization/zig-builds/) 版本发布工具 GoReleaser 支持了 Zig diff --git a/content/post/0.14.smd b/content/post/0.14.smd index de8aa00..ef2f158 100644 --- a/content/post/0.14.smd +++ b/content/post/0.14.smd @@ -8,11 +8,11 @@ https://ziglang.org/download/0.14.0/release-notes.html -# [发布概览]($section.id('release-overview')) +# [发布概览]($heading.id('release-overview')) Zig 0.14.0 版本是经过 **9 个月的工作**,由 **251 位不同的贡献者** 完成,包含 **3467 个提交** 的成果。该版本专注于提升 **健壮性**、**最优性** 和 **可重用性**,并通过 Zig 软件基金会 (Zig Software Foundation) 资助开发。 -# [核心主题与重要更新]($section.id('key-topics-and-updates')) +# [核心主题与重要更新]($heading.id('key-topics-and-updates')) 1. **提升编译速度与开发效率:** @@ -124,13 +124,13 @@ Zig 0.14.0 版本是经过 **9 个月的工作**,由 **251 位不同的贡献 - 最终目标是达到 1.0.0 版本,届时 Tier 1 支持将包含 bug 政策。 -# [关于 Zig 0.14.0 版本的常见问题解答]($section.id('faq-zig-0-14-0')) +# [关于 Zig 0.14.0 版本的常见问题解答]($heading.id('faq-zig-0-14-0')) -# [Zig 0.14.0 版本的主要更新和亮点是什么?]($section.id('main-updates-and-highlights')) +## [Zig 0.14.0 版本的主要更新和亮点是什么?]($heading.id('main-updates-and-highlights')) Zig 0.14.0 版本是长达 9 个月开发工作和 3467 次提交的成果,主要亮点包括:显著增强了对多种目标平台的支持,包括 arm/thumb、mips/mips64、powerpc/powerpc64、riscv32/riscv64 和 s390x 等,许多之前存在工具链问题、标准库支持缺失或崩溃的情况现在应该可以正常工作了。此外,该版本在构建系统方面进行了大量升级,并对语言进行了多项重要改进,例如引入了 Labeled Switch 和 Decl Literals 等新特性。为了缩短编辑/编译/调试周期,版本还迈向了两个长期投资目标:增量编译和快速 x86 后端。 -# [Zig 如何对不同目标平台的开发支持进行分级?]($section.id('target-support-tiers')) +## [Zig 如何对不同目标平台的开发支持进行分级?]($heading.id('target-support-tiers')) Zig 使用四层系统来对不同目标平台的支持级别进行分类,其中 Tier 1 是最高级别: @@ -139,15 +139,15 @@ Zig 使用四层系统来对不同目标平台的支持级别进行分类,其 - **Tier 3:** 编译器可以依赖外部后端(如 LLVM)为该目标平台生成机器代码。链接器可以为该目标平台生成目标文件、库和可执行文件。 - **Tier 4:** 编译器可以依赖外部后端(如 LLVM)为该目标平台生成汇编源代码。如果 LLVM 将此目标平台视为实验性,则需要从源代码构建 LLVM 和 Zig 才能使用它。 -# [什么是 Labeled Switch,它有什么优势?]($section.id('labeled-switch-advantages')) +## [什么是 Labeled Switch,它有什么优势?]($heading.id('labeled-switch-advantages')) Labeled Switch 是 Zig 0.14.0 中引入的一项语言特性,允许 switch 语句被标记,并作为 continue 语句的目标。continue :label value 语句会用 value 替换原始的 switch 表达式操作数,并重新评估 switch。尽管在语义上类似于循环中的 switch,但 Labeled Switch 的关键优势在于其代码生成特性。它可以生成帮助 CPU 更准确预测分支的代码,从而提高热循环中的性能,特别是在处理指令分派、评估有限状态自动机 (FSA) 或执行类似基于 case 的评估时。这有助于 branch predictor 更准确地预测控制流。 -# [Decl Literals 是什么,它解决了哪些问题?]($section.id('decl-literals-explanation')) +## [Decl Literals 是什么,它解决了哪些问题?]($heading.id('decl-literals-explanation')) Decl Literals 是 Zig 0.14.0 扩展 "enum literal" 语法 (.foo) 而引入的新特性。现在,一个枚举字面量 .foo 不仅可以引用枚举变体,还可以使用 Result Location Semantics 引用目标类型上的任何声明(const/var/fn)。这在初始化结构体字段时特别有用,可以避免重复指定类型,并有助于避免 Faulty Default Field Values 的问题,确保数据不变量不会因覆盖单个字段而受到破坏。它也支持直接调用函数来初始化值。 -# [Zig 0.14.0 版本在内存分配器方面有哪些值得关注的变化?]($section.id('allocator-changes')) +## [Zig 0.14.0 版本在内存分配器方面有哪些值得关注的变化?]($heading.id('allocator-changes')) 该版本对内存分配器进行了多项改进: @@ -156,7 +156,7 @@ Decl Literals 是 Zig 0.14.0 扩展 "enum literal" 语法 (.foo) 而引入的新 - **Allocator API Changes (remap):** std.mem.Allocator.VTable 引入了一个新的 remap 函数,允许尝试扩展或收缩内存并可能重新定位,如果无法在不执行内部 memcpy 的情况下完成,则返回 null,提示调用者自行处理复制。同时,resize 函数保持不变。Allocator.VTable 中的所有函数现在使用 std.mem.Alignment 类型代替 u8,增加了类型安全。 - **Runtime Page Size:** 移除了编译时已知的 std.mem.page\_size,代之以编译时已知的页面大小上下界 std.heap.page\_size\_min 和 std.heap.page\_size\_max。运行时获取页面大小可以使用 std.heap.pageSize(),它会优先使用编译时已知的值,否则在运行时查询操作系统并缓存结果。这修复了对 Asahi Linux 等新硬件上运行 Linux 的支持。 -# [Zig 0.14.0 版本如何改进构建系统,特别是处理模块和依赖关系?]($section.id('build-system-improvements')) +## [Zig 0.14.0 版本如何改进构建系统,特别是处理模块和依赖关系?]($heading.id('build-system-improvements')) Zig 0.14.0 版本在构建系统方面有多项重要改进: @@ -166,7 +166,7 @@ Zig 0.14.0 版本在构建系统方面有多项重要改进: - **addLibrary Function:** 引入 addLibrary 函数作为 addSharedLibrary 和 addStaticLibrary 的替代,允许在 build.zig 中更容易地切换链接模式,并与 linkLibrary 函数名称保持一致。 - **Import ZON:** ZON 文件现在可以在编译时通过 @import("foo.zon") 导入,前提是结果类型已知。 -# [Zig 0.14.0 版本在编译器后端和编译速度方面有哪些进展?]($section.id('compiler-backend-and-speed')) +## [Zig 0.14.0 版本在编译器后端和编译速度方面有哪些进展?]($heading.id('compiler-backend-and-speed')) 该版本在编译器后端和编译速度方面取得了进展: @@ -174,7 +174,7 @@ Zig 0.14.0 版本在构建系统方面有多项重要改进: - **Incremental Compilation:** 引入了增量编译特性,可以通过 -fincremental 标志启用。尽管尚未默认启用,但结合文件系统监听,可以显著缩短修改代码后的重新分析时间,提供快速的编译错误反馈。 - **x86 Backend:** x86 后端在行为测试套件中的通过率已接近 LLVM 后端,并且在开发时通常比 LLVM 后端提供更快的编译速度和更好的调试器支持。虽然尚未默认选中,但鼓励用户尝试使用 -fno-llvm 或在构建脚本中设置 use\_llvm = false 来启用。 -# [Zig 0.14.0 版本在工具链和运行时方面有哪些值得注意的更新?]($section.id('toolchain-and-runtime-updates')) +## [Zig 0.14.0 版本在工具链和运行时方面有哪些值得注意的更新?]($heading.id('toolchain-and-runtime-updates')) 该版本在工具链和运行时方面也有多项更新: @@ -189,6 +189,6 @@ Zig 0.14.0 版本在构建系统方面有多项重要改进: - **Optimized memcpy:** 提供了优化的 memcpy 实现,不再捆绑 musl 的 memcpy 文件。 - **Integrated Fuzzer:** 集成了 alpha 质量的 fuzzer,可以通过 --fuzz CLI 选项使用,并提供一个 fuzzer Web UI 显示实时代码覆盖率。 -# [总结]($section.id('summary')) +# [总结]($heading.id('summary')) Zig 0.14.0 版本是向 1.0.0 版本迈进的重要一步,在性能优化(尤其是编译速度)、跨平台支持、语言特性和标准库方面都带来了显著改进。增量编译和快速 x86 后端是关键的长期投资,旨在提升开发者体验。新语言特性如 Labeled Switch 和 Decl Literals 提供了更强大和安全的编程模式。标准库的重组和容器的调整反映了社区的使用模式和最佳实践。构建系统也获得了重要升级,使模块管理和依赖处理更加灵活。尽管仍存在已知 bug,但 Zig 社区在本次发布中展示了活跃的开发和持续的进步。 diff --git a/content/post/2023-09-05-bog-gc-1-en.smd b/content/post/2023-09-05-bog-gc-1-en.smd index 238d2a7..c97ab6f 100644 --- a/content/post/2023-09-05-bog-gc-1-en.smd +++ b/content/post/2023-09-05-bog-gc-1-en.smd @@ -10,11 +10,11 @@ }, --- -# [Bog GC Design]($section.id('bog-gc-design')) +# [Bog GC Design]($heading.id('bog-gc-design')) Bog is a small scripting language developed using Zig. Its GC design is inspired by a paper titled [An efficient of Non-Moving GC for Function languages](https://www.pllab.riec.tohoku.ac.jp/papers/icfp2011UenoOhoriOtomoAuthorVersion.pdf). -# [Overview]($section.id('Overview')) +# [Overview]($heading.id('overview')) 1. Introduction - Design of the Heap @@ -22,7 +22,7 @@ Bog is a small scripting language developed using Zig. Its GC design is inspired - Design of Bitmap 2. Implementation -# [Introduction]($section.id('Introduction')) +# [Introduction]($heading.id('introduction')) GC stands for garbage collection, which is primarily a memory management strategy for the `heap` region. Memory allocations in the heap are done in exponentially increasing sizes, with a special sub-heap dedicated solely to very large objects. One advantage of this approach might be its efficiency in handling memory requests of various sizes. @@ -71,13 +71,13 @@ graph TD +------------+ ``` -## [Memory Sub-Heap Pool]($section.id('Memory Sub-Heap Pool')) +## [Memory Sub-Heap Pool]($heading.id('memory-sub-heap-pool')) We've designed a memory resource pool. This pool consists of numerous allocation segments of fixed size. It means that, regardless of how much space a sub-heap requests, it will request it in units of these fixed-size "segments". For instance, if the allocation segments in the pool are of size 1MB, then a sub-heap might request space in sizes of 1MB, 2MB, 3MB, etc., rather than requesting non-integer multiples like 1.5MB or 2.5MB. The dynamic allocation and reclaiming of space by the sub-heaps from a resource pool made up of fixed-size segments provide greater flexibility and may enhance the efficiency of memory utilization. -## [Types of GC]($section.id('Types of GC')) +## [Types of GC]($heading.id('types-of-gc')) There are many common types of GC. @@ -136,7 +136,7 @@ However, Non-Moving GC has its disadvantages: To address these problems requires many complicated steps, which won't be elaborated on here. We'll focus on Bog's GC for the explanation. -## [Meta Bitmap]($section.id('Meta Bitmap')) +## [Meta Bitmap]($heading.id('meta-bitmap')) "Meta Bitmap" or "meta-level bitmaps". This is a higher-level bitmap that summarizes the contents of the original bitmap. This hierarchical structure is similar to the inode mapping in file systems or the use of multi-level page tables in computer memory management. @@ -150,11 +150,11 @@ Let's design for a 32-bit architecture. A 32-bit architecture means that the com For example, if a bitmap is 320 bits long, then on a 32-bit architecture, the worst-case scenario might require checking 10 blocks of 32 bits to find a free bit. This can be represented by log32(320), which results in 10. -## [Bitmap]($section.id('Bitmap')) +## [Bitmap]($heading.id('bitmap')) Since Bog's GC is essentially still based on "Mark-Sweep", using bitmaps to record data is indispensable. In Bog, we adopted the method of "bitmap records data" for GC. And to improve efficiency, we introduced the concept of meta-bitmaps, where every 4 elements correspond to a meta-bitmap, recording the occupancy status of multiple spaces, and increasing the depth based on the object age in the heap. -## [Implementation]($section.id('implementation')) +## [Implementation]($heading.id('implementation')) In reality, Bog's design is a bit more complex. Here are sample in practical code: diff --git a/content/post/2023-09-05-bog-gc-1.smd b/content/post/2023-09-05-bog-gc-1.smd index 8245624..cc7374e 100644 --- a/content/post/2023-09-05-bog-gc-1.smd +++ b/content/post/2023-09-05-bog-gc-1.smd @@ -10,11 +10,11 @@ }, --- -# [Bog GC Design -- Concepts]($section.id('bog-gc-design-concepts')) +# [Bog GC Design -- Concepts]($heading.id('bog-gc-design-concepts')) [Bog](https://github.com/Vexu/bog) 是一款基于 Zig 开发的小型脚本语言。它的 GC 设计受到一篇论文[An efficient of Non-Moving GC for Function languages](https://www.pllab.riec.tohoku.ac.jp/papers/icfp2011UenoOhoriOtomoAuthorVersion.pdf)的启发。 -# [Summary]($section.id('summary')) +# [Summary]($heading.id('summary')) GC 是一种垃圾回收的机制,主要是针对`heap`区域的内存管理策略。在堆中的内存分配是按照指数级增长的大小进行的,此外还有一个专门用于非常大对象的特殊子堆。这种方法的一个优点可能是它可以高效地处理各种大小的内存请求。 @@ -63,13 +63,13 @@ graph TD +------------+ ``` -## [Memory Sub-Heap Pool]($section.id('memory-sub-heap-pool')) +## [Memory Sub-Heap Pool]($heading.id('memory-sub-heap-pool')) 我们设计一个内存的资源池。这个池由许多固定大小的分配段组成。这意味着,无论子堆请求多少空间,它都会以这些固定大小的"段"为单位来请求。例如,如果池中的分配段大小为 1MB,那么一个子堆可能会请求 1MB、2MB、3MB 等大小的空间,而不是请求 1.5MB 或 2.5MB 这样的非整数倍的大小。 而子堆从一个由固定大小的段组成的资源池中动态分配和回收空间,这种策略可以提供更高的灵活性,并可能提高内存使用的效率。 -## [Types of GC]($section.id('types-of-gc')) +## [Types of GC]($heading.id('types-of-gc')) 常见的 GC 有很多类型。 @@ -123,7 +123,7 @@ Cheney 的拷贝收集器: 是一种用于半区(semi-space)的拷贝垃圾 为了解决这些问题需要很多复杂的步骤,在此不多赘述。单以 Bog 的 GC 来讲解。 -## [Meta Bitmap]($section.id('meta-bitmap')) +## [Meta Bitmap]($heading.id('meta-bitmap')) "元级位图"或"meta-level bitmaps"。这是一个更高级别的位图,用于汇总原始位图的内容。这种层次化的结构类似于文件系统中的 inode 映射或多级页表在计算机内存管理中的使用。 @@ -137,11 +137,11 @@ Cheney 的拷贝收集器: 是一种用于半区(semi-space)的拷贝垃圾 例如,如果一个位图有 320 位,那么在 32 位架构上,最坏的情况可能需要检查 10 个 32 位块才能找到一个空闲位。这可以通过 $\log_{32}(320)$来表示,结果是 10。 -## [Bitmap]($section.id('bitmap')) +## [Bitmap]($heading.id('bitmap')) 由于 Bog 的 GC 本质上还是采用了"标记-清除",所以利用位图来记录数据是必不可少的。在 Bog 中,我们采用了"位图记录数据"的方式来进行 GC。而为了提高效率,我们增加了元位图的概念,即每 4 个元素对应一个元位图,用于记录多空间的占用状态,并且根据 heap 的对象时间增加深度。 -## [Implementation]($section.id('implementation')) +## [Implementation]($heading.id('implementation')) 实际上,在 Bog 的设计中,要更加复杂一些。我们增加了 diff --git a/content/post/2023-09-05-hello-world.smd b/content/post/2023-09-05-hello-world.smd index 7df8808..7a34645 100644 --- a/content/post/2023-09-05-hello-world.smd +++ b/content/post/2023-09-05-hello-world.smd @@ -11,7 +11,7 @@ - [ZigCC 网站](https://ziglang.cc) - [ZigCC 公众号](https://github.com/zigcc/.github/raw/main/zig_mp.png) -# [供稿方式]($section.id('contribute')) +# [供稿方式]($heading.id('contribute')) 1. Fork 仓库 https://github.com/zigcc/zigcc.github.io 2. 在 `content/post` 内添加自己的文章(smd格式),文件命名为: `${YYYY}-${MM}-${DD}-${SLUG}.smd` @@ -31,6 +31,6 @@ --- ``` -# [本地预览]($section.id('local-preview')) +# [本地预览]($heading.id('local-preview')) 在写完文章后,可以使用 [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 c79e869..06d3e41 100644 --- a/content/post/2023-09-21-zig-midi.smd +++ b/content/post/2023-09-21-zig-midi.smd @@ -23,7 +23,7 @@ MIDI 是"乐器数字接口"的缩写,是一种用于音乐设备之间通信 ├── midi.zig ``` -# [基础]($section.id('basic')) +# [基础]($heading.id('basic')) 在 MIDI 协议中,`0xFF` 是一个特定的状态字节,用来表示元事件(Meta Event)的开始。元事件是 MIDI 文件结构中的一种特定消息,通常不用于实时音频播放,但它们包含有关 MIDI 序列的元数据,例如序列名称、版权信息、歌词、时间标记、速度(BPM)更改等。 @@ -56,7 +56,7 @@ MIDI 是"乐器数字接口"的缩写,是一种用于音乐设备之间通信 元事件主要存在于 MIDI 文件中,特别是在标准 MIDI 文件 (SMF) 的上下文中。在实时 MIDI 通信中,元事件通常不会被发送,因为它们通常不会影响音乐的实际播放。 -# [Midi.zig]($section.id('Midi.zig')) +# [Midi.zig]($heading.id('midizig')) 本文件主要是处理 MIDI 消息的模块,为处理 MIDI 消息提供了基础结构和函数。 @@ -194,7 +194,7 @@ pub const Message = struct { - value 和 setValue 函数:用于获取和设置 MIDI 消息的值字段。 - Kind 枚举:定义了 MIDI 消息的所有可能种类,包括通道事件和系统事件。 -## [midi 消息结构]($section.id('midi 消息结构')) +## [midi 消息结构]($heading.id('midi-message-structure')) 我们需要先了解 MIDI 消息的一些背景。 @@ -211,7 +211,7 @@ pub const Message = struct { 4. **设置值**:`message.values = .{ ... }` 将这两个 7 位的值设置到 `message.values` 中。 -## [事件]($section.id('事件')) +## [事件]($heading.id('event')) 针对事件,我们看 enum。 @@ -251,7 +251,7 @@ pub const Message = struct { 以下是对每个枚举成员的简要说明: -### [频道事件 (Channel events)]($section.id('频道事件 (Channel events)')) +### [频道事件 (Channel events)]($heading.id('channel-events')) 1. **NoteOff**:这是一个音符结束事件,表示某个音符不再播放。 2. **NoteOn**:这是一个音符开始事件,表示开始播放某个音符。 @@ -261,7 +261,7 @@ pub const Message = struct { 6. **ChannelPressure**:频道压力事件,与多声道键盘压力相似,但它适用于整个频道,而不是特定音符。 7. **PitchBendChange**:音高弯曲变更事件,表示音符音高的上升或下降。 -### [系统事件 (System events)]($section.id('系统事件 (System events)')) +### [系统事件 (System events)]($heading.id('system-events')) 1. **ExclusiveStart**:独占开始事件,标志着一个独占消息序列的开始。 2. **MidiTimeCodeQuarterFrame**:MIDI 时间码四分之一帧,用于同步与其他设备。 @@ -276,11 +276,11 @@ pub const Message = struct { 11. **ActiveSensing**:活动感知事件,是一种心跳信号,表示设备仍然在线并工作。 12. **Reset**:重置事件,用于将设备重置为其初始状态。 -### [其他]($section.id('其他')) +### [其他]($heading.id('other')) 1. **Undefined**:未定义事件,可能表示一个未在此枚举中定义的或无效的 MIDI 事件。 -# [decode.zig]($section.id('decode.zig')) +# [decode.zig]($heading.id('decodezig')) 本文件是对 MIDI 文件的解码器, 提供了一组工具,可以从不同的输入源解析 MIDI 文件的各个部分。这样可以方便地读取和处理 MIDI 文件。 @@ -498,7 +498,7 @@ pub fn file(reader: anytype, allocator: *mem.Allocator) !midi.File { 8. trackEvent: 从 reader 中解析一个 MIDI 轨道事件。它可以是 MIDI 消息或元事件。 9. file: 用于从 reader 解码一个完整的 MIDI 文件。它首先解码文件头,然后解码所有的文件块。这个函数会返回一个表示 MIDI 文件的结构体。 -## [message 解析]($section.id('message 解析')) +## [message 解析]($heading.id('message-analysis')) ```zig const status_byte = if (statusByte(first_byte.?)) |status_byte| blk: { @@ -531,7 +531,7 @@ const status_byte = if (statusByte(first_byte.?)) |status_byte| blk: { 4. `else return error.InvalidMessage;`: 如果 `first_byte` 不是状态字节,并且不存在前一个消息,那么返回一个 `InvalidMessage` 错误。 -# [encode.zig]($section.id('encode.zig')) +# [encode.zig]($heading.id('encodezig')) 本文件用于将 MIDI 数据结构编码为其对应的二进制形式。具体来说,它是将内存中的 MIDI 数据结构转换为 MIDI 文件格式的二进制数据。 @@ -672,7 +672,7 @@ pub fn file(writer: anytype, f: midi.File) !void { - file 函数:这是主函数,用于将整个 MIDI 文件数据结构编码为其二进制形式。它首先编码文件头,然后循环编码每个块和块中的事件。 -## [int 函数]($section.id('int 函数')) +## [int 函数]($heading.id('int-function')) ```zig @@ -731,7 +731,7 @@ pub fn int(writer: anytype, i: u28) !void { - 使用提供的`writer`将翻转后的字节写入到目标位置。 -# [file.zig]($section.id('file.zig')) +# [file.zig]($heading.id('filezig')) 主要目的是为了表示和处理 MIDI 文件的不同部分,以及提供了一个迭代器来遍历 MIDI 轨道的事件。 @@ -912,7 +912,7 @@ pub const TrackIterator = struct { - 定义了一个 Result 结构来返回事件和关联的数据。 - 提供了一个`next`方法来读取下一个事件。 -# [Build.zig]($section.id('build-zig')) +# [Build.zig]($heading.id('build-zig')) buid.zig 是一个 Zig 构建脚本(build.zig),用于配置和驱动 Zig 的构建过程。 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 7fba69d..09402c4 100644 --- a/content/post/2023-12-24-zig-build-explained-part1.smd +++ b/content/post/2023-12-24-zig-build-explained-part1.smd @@ -15,11 +15,11 @@ Zig 构建系统仍然缺少文档,对很多人来说,这是不使用它的 我们将从一个刚刚初始化的 Zig 项目开始,逐步深入到更复杂的项目。在此过程中,我们将学习如何使用库和软件包、添加 C 代码,甚至如何创建自己的构建步骤。 -# [免责声明]($section.id('免责声明')) +# [免责声明]($heading.id('disclaimer')) 由于我不会解释 Zig 语言的语法或语义,因此我希望你至少已经有了一些使用 Zig 的基本经验。我还将链接到标准库源代码中的几个要点,以便您了解所有这些内容的来源。我建议你阅读编译系统的源代码,因为如果你开始挖掘编译脚本中的函数,大部分内容都不言自明。所有功能都是在标准库中实现的,不存在隐藏的构建魔法。 -# [开始]($section.id('开始')) +# [开始]($heading.id('began')) 我们通过新建一个文件夹来创建一个新项目,并在该文件夹中调用 zig init-exe。 @@ -56,7 +56,7 @@ pub fn build(b: *std.Build) void { } ``` -# [基础知识]($section.id('基础知识')) +# [基础知识]($heading.id('basic-knowledge')) 构建系统的核心理念是,Zig 工具链将编译一个 Zig 程序 (build.zig),该程序将导出一个特殊的入口点(`pub fn build(b: *std.build.Builder) void`),当我们调用 `zig build` 时,该入口点将被调用。 @@ -95,7 +95,7 @@ Step 遵循与 std.mem.Allocator 相同的接口模式,需要实现一个 make 现在,我们需要创建一个稍正式的 Zig 程序: -# [编译 Zig 源代码]($section.id('编译 Zig 源代码')) +# [编译 Zig 源代码]($heading.id('compile-zig-source-code')) 要使用编译系统编译可执行文件,编译器需要使用函数 Builder.addExecutable,它将为我们创建一个新的 LibExeObjStep。这个步骤实现是 zig build-exe、zig build-lib、zig build-obj 或 zig test 的便捷封装,具体取决于初始化方式。本文稍后将对此进行详细介绍。 @@ -118,7 +118,7 @@ pub fn build(b: *std.build.Builder) void { 这将始终在当前机器的调试模式下编译,因此对于初学者来说,这可能就足够了。但如果你想开始发布你的项目,你可能需要启用交叉编译: -# [交叉编译]($section.id('交叉编译')) +# [交叉编译]($heading.id('cross-compiling')) 交叉编译是通过设置程序的目标和编译模式来实现的 @@ -187,7 +187,7 @@ zig build -Doptimize=ReleaseSmall 但我们仍然必须调用 zig build 编译,因为默认调用仍然没有任何作用。让我们改变一下! -# [安装工件]($section.id('安装工件')) +# [安装工件]($heading.id('installation-artifacts')) 要安装任何东西,我们必须让它依赖于构建器的安装步骤。该步骤是已创建的,可通过 Builder.getInstallStep() 访问。我们还需要创建一个新的 InstallArtifactStep,将我们的 exe 文件复制到安装目录(通常是 zig-out) @@ -254,7 +254,7 @@ pub fn build(b: *std.build.Builder) void { 现在,从理解初始构建脚本到完全扩展,还缺少一个部分: -# [运行已构建的应用程序]($section.id('运行已构建的应用程序')) +# [运行已构建的应用程序]($heading.id('run-the-built-application')) 为了开发用户体验和一般便利性,从构建脚本中直接运行程序是非常实用的。这通常是通过运行步骤实现的,可以通过 zig build run 调用。 @@ -328,7 +328,7 @@ pub fn build(b: *std.build.Builder) void { zig build run -- -o foo.bin foo.asm ``` -# [结论]($section.id('conclusion')) +# [结论]($heading.id('conclusion')) 本系列的第一章应该能让你完全理解本文开头的构建脚本,并能创建自己的构建脚本。 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 44a44bd..e16f474 100644 --- a/content/post/2023-12-28-zig-build-explained-part2.smd +++ b/content/post/2023-12-28-zig-build-explained-part2.smd @@ -9,15 +9,15 @@ > - 原文链接: https://zig.news/xq/zig-build-explained-part-2-1850 > - API 适配到 Zig 0.11.0 版本 -# [注释]($section.id('注释')) +# [注释]($heading.id('annotation')) 从现在起,我将只提供一个最精简的 build.zig,用来说明解决一个问题所需的步骤。如果你想了解如何将所有这些文件粘合到一个构建文件中,请阅读本系列[第一篇文章](2023-12-24-zig-build-explained-part1)。 -# [在命令行上编译 C 代码]($section.id('在命令行上编译 C 代码')) +# [在命令行上编译 C 代码]($heading.id('compile-c-code-on-the-command-line')) Zig 有两种编译 C 代码的方法,而且这两种很容易混淆。 -## [使用 zig cc]($section.id('使用 zig cc')) +## [使用 zig cc]($heading.id('using-zig-cc')) Zig 提供了 LLVM c 编译器 clang。第一种是 zig cc 或 zig c++,它是与 clang 接近 1:1 的前端。由于我们无法直接从 build.zig 访问这些功能(而且我们也不需要!),所以我将在快速的介绍这个主题。 @@ -41,7 +41,7 @@ zig cc -o example.exe -target x86_64-windows-gnu buffer.c main.c 如你所见,只需向 -target 传递目标三元组,就能调用交叉编译。只需确保所有外部库都已准备好进行交叉编译即可! -# [使用 zig build-exe 和其他工具]($section.id('使用 zig build-exe 和其他工具')) +# [使用 zig build-exe 和其他工具]($heading.id('use-zig-build-exe-and-other-tools')) 使用 Zig 工具链构建 C 项目的另一种方法与构建 Zig 项目的方法相同: @@ -59,7 +59,7 @@ zig build-exe -lc -target x86_64-windows-gnu main.c buffer.c 你会发现,使用这条编译命令,Zig 会自动在输出文件中附加 .exe 扩展名,并生成 .pdb 调试数据库。如果你在此处传递 --name example,输出文件也会有正确的 .exe 扩展名,所以你不必考虑这个问题。 -# [用 build.zig 创建 C 代码]($section.id('用 build.zig 创建 C 代码')) +# [用 build.zig 创建 C 代码]($heading.id('create-c-code-with-buildzig')) 那么,我们如何用 build.zig 来构建上面的两个示例呢? @@ -112,7 +112,7 @@ exe.addCSourceFile(.{ .file = std.build.LazyPath.relative("buffer.c"), .flags = exe.addCSourceFile(.{.file = std.build.LazyPath.relative("buffer.c"), .flags = &.{"-fno-sanitize=undefined"}}); ``` -# [使用外部库]($section.id('使用外部库')) +# [使用外部库]($heading.id('using-external-libraries')) 通常情况下,C 项目依赖于其他库,这些库通常预装在 Unix 系统中,或通过软件包管理器提供。 @@ -181,7 +181,7 @@ zig build ./zig-out/bin/downloader https://mq32.de/public/ziggy.txt ``` -# [配置路径]($section.id('配置路径')) +# [配置路径]($heading.id('configuration-path')) 由于我们不能在交叉编译项目中使用 pkg-config,或者我们想使用预编译的专用库(如 BASS 音频库),因此我们需要配置包含路径和库路径。 @@ -220,7 +220,7 @@ pub fn build(b: *std.Build) void { addIncludePath 和 addLibraryPath 都可以被多次调用,以向编译器添加多个路径。这些函数不仅会影响 C 代码,还会影响 Zig 代码,因此 @cImport 可以访问包含路径中的所有头文件。 -# [每个文件的包含路径]($section.id('每个文件的包含路径')) +# [每个文件的包含路径]($heading.id('include-path-for-each-file')) 因此,如果我们需要为每个 C 文件设置不同的包含路径,我们就需要用不同的方法来解决这个问题: 由于我们仍然可以通过 addCSourceFile 传递任何 C 编译器标志,因此我们也可以在这里手动设置包含目录。 @@ -252,7 +252,7 @@ pub fn build(b: *std.Build) void { 上面的示例非常简单,所以你可能会想为什么需要这样的东西。答案是,有些库的头文件名称非常通用,如 api.h 或 buffer.h,而您希望使用两个共享头文件名称的不同库。 -# [构建 C++ 项目]($section.id('构建 C++ 项目')) +# [构建 C++ 项目]($heading.id('build-c-projects')) 到目前为止,我们只介绍了 C 文件,但构建 C++ 项目并不难。你仍然可以使用 addCSourceFile,但只需传递一个具有典型 C++ 文件扩展名的文件,如 cpp、cxx、c++ 或 cc: @@ -285,7 +285,7 @@ pub fn build(b: *std.Build) void { 这就是构建 C++ 文件所需的全部知识,没有什么更神奇的了。 -# [指定语言版本]($section.id('指定语言版本')) +# [指定语言版本]($heading.id('specify-language-version')) 试想一下,如果你创建了一个庞大的项目,其中的 C 或 C++ 文件有新有旧,而且可能是用不同的语言标准编写的。为此,我们可以使用编译器标志来传递 -std=c90 或 -std=c++98: @@ -320,7 +320,7 @@ pub fn build(b: *std.Build) void { } ``` -# [条件编译]($section.id('条件编译')) +# [条件编译]($heading.id('conditional-compilation')) 与 Zig 相比,C 和 C++ 的条件编译方式非常繁琐。由于缺乏惰性求值的功能,有时必须根据目标环境来包含/排除文件。你还必须提供宏定义来启用/禁用某些项目功能。 @@ -373,7 +373,7 @@ pub fn build(b: *std.Build) void { 有条件地包含文件就像使用 if 一样简单,你可以这样做。只要不根据你想在构建脚本中定义的任何约束条件调用 addCSourceFile 即可。只包含特定平台的文件?看看上面的脚本就知道了。根据系统时间包含文件?也许这不是个好主意,但还是有可能的! -# [编译大型项目]($section.id('编译大型项目')) +# [编译大型项目]($heading.id('compile-large-projects')) 由于大多数 C(更糟糕的是 C++)项目都有大量文件(SDL2 有 411 个 C 文件和 40 个 C++ 文件),我们必须找到一种更简单的方法来编译它们。调用 addCSourceFile 400 次并不能很好地扩展。 @@ -478,7 +478,7 @@ pub fn build(b: *std.build.Builder) !void { 注意:其他构建系统会考虑文件名,而 Zig 系统不会!例如,在一个 qmake 项目中不能有两个名为 data.c 的文件!Zig 并不在乎,你可以添加任意多的同名文件,只要确保它们在不同的文件夹中就可以了 😏。 -# [编译 Objective C]($section.id('编译 Objective C')) +# [编译 Objective C]($heading.id('compiled-objective-c')) 我完全忘了!Zig 不仅支持编译 C 和 C++,还支持通过 clang 编译 Objective C! @@ -514,7 +514,7 @@ pub fn build(b: *std.Build) void { 在这里,链接 libc 是隐式的,因为添加框架会自动强制链接 libc。是不是很酷? -# [混合使用 C 和 Zig 源代码]($section.id('混合使用 C 和 Zig 源代码')) +# [混合使用 C 和 Zig 源代码]($heading.id('mixing-c-and-zig-source-code')) 现在,是最后一章: 混合 C 代码和 Zig 代码! @@ -554,7 +554,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 语言! -# [结论]($section.id('conclusion')) +# [结论]($heading.id('conclusion')) 假设你只编译一个输出文件,那么现在你应该可以将几乎所有的 C/C++ 项目移植到 build.zig。 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 e7a962e..9adc21e 100644 --- a/content/post/2023-12-29-zig-build-explained-part3.smd +++ b/content/post/2023-12-29-zig-build-explained-part3.smd @@ -11,17 +11,17 @@ 从现在起,我将只提供一个最精简的 build.zig,用来说明解决一个问题所需的步骤。如果你想了解如何将所有这些文件粘合到一个构建文件中,请阅读本系列[第一篇文章](2023-12-24-zig-build-explained-part1)。 -# [复合项目]($section.id('复合项目')) +# [复合项目]($heading.id('composite-project')) 有很多简单的项目只包含一个可执行文件。但是,一旦开始编写库,就必须对其进行测试,通常会编写一个或多个示例应用程序。当人们开始使用外部软件包、C 语言库、生成代码等时,复杂性也会随之上升。 本文试图涵盖所有这些用例,并将解释如何使用 build.zig 来编写多个程序和库。 -# [软件包]($section.id('软件包')) +# [软件包]($heading.id('software-package')) 译者:此处代码和说明,需要 zig build-exe --pkg-begin,但是在 0.11 已经失效。所以删除。 -# [库]($section.id('库')) +# [库]($heading.id('library')) 但 Zig 也知道库这个词。但我们不是已经讨论过外部库了吗? @@ -36,7 +36,7 @@ 在 Zig 中,我们需要导入库的头文件,如果头文件在 Zig 中,则使用包,如果是 C 语言头文件,则使用 @cImport。 -# [工具]($section.id('工具')) +# [工具]($heading.id('tool')) 如果我们的项目越来越多,那么在构建过程中就需要使用工具。这些工具通常会完成以下任务: @@ -48,7 +48,7 @@ 但我们如何在 build.zig 中完成这些工作呢? -# [添加软件包]($section.id('添加软件包')) +# [添加软件包]($heading.id('add-pack')) 添加软件包通常使用 LibExeObjStep 上的 addPackage 函数。该函数使用一个 std.build.Pkg 结构来描述软件包的外观: @@ -98,7 +98,7 @@ exe.addModule("lola",pkgs.lola); exe.addModule("args",pkgs.args); ``` -# [添加库]($section.id('添加库')) +# [添加库]($heading.id('add-library')) 添加库相对容易,但我们需要配置更多的路径。 @@ -106,7 +106,7 @@ exe.addModule("args",pkgs.args); 假设我们要将 libcurl 链接到我们的项目,因为我们要下载一些文件。 -## [系统库]($section.id('系统库')) +## [系统库]($heading.id('system-library')) 对于 unixoid 系统,我们通常可以使用系统软件包管理器来链接系统库。方法是调用 linkSystemLibrary,它会使用 pkg-config 自行找出所有路径: @@ -137,7 +137,7 @@ pub fn build(b: *std.Build) void { 对于 Linux 系统,这是链接外部库的首选方式。 -## [本地库]($section.id('本地库')) +## [本地库]($heading.id('native-library')) 不过,您也可以链接您作为二进制文件提供商的库。为此,我们需要调用几个函数。首先,让我们来看看这样一个库是什么样子的: @@ -167,7 +167,7 @@ include 我们可以看到,vendor/libcurl/include 路径包含我们的头文件,vendor/libcurl/lib 文件夹包含一个静态库(libcurl.a)和一个共享/动态库(libcurl.so)。 -## [动态链接]($section.id('动态链接')) +## [动态链接]($heading.id('dynamic-link')) 要链接 libcurl,我们需要先添加 include 路径,然后向 zig 提供库的前缀和库名:(todo 代码有待验证,因为 curl 可能需要自己编译自己生成 static lib) @@ -197,7 +197,7 @@ addLibraryPath 对库文件也有同样的作用。这意味着 Zig 现在也会 最后,linkSystemLibrary 会告诉 Zig 搜索名为 "curl "的库。如果你留心观察,就会发现上面列表中的文件名是 libcurl.so,而不是 curl.so。在 unixoid 系统中,库文件的前缀通常是 lib,这样就不会将其传递给系统。在 Windows 系统中,库文件的名字应该是 curl.lib 或类似的名字。 -# [静态链接]($section.id('静态链接')) +# [静态链接]($heading.id('static-link')) 当我们要静态链接一个库时,我们必须采取一些不同的方法: @@ -250,7 +250,7 @@ pub fn build(b: *std.build.Builder) void { 我们可以继续静态链接越来越多的库,并拉入完整的依赖关系树。 -# [通过源代码链接库]($section.id('通过源代码链接库')) +# [通过源代码链接库]($heading.id('link-library-via-source-code')) 不过,我们还有一种与 Zig 工具链截然不同的链接库方式: @@ -293,7 +293,7 @@ pub fn build(b: *std.build.Builder) void { 这一点尤其方便,因为我们可以使用 setTarget 和 setBuildMode 从任何地方编译到任何地方。 -# [使用工具]($section.id('使用工具')) +# [使用工具]($heading.id('use-tooling')) 在工作流程中使用工具,通常是在需要以 bison、flex、protobuf 或其他形式进行预编译时。工具的其他用例包括将输出文件转换为不同格式(如固件映像)或捆绑最终应用程序。 @@ -352,7 +352,7 @@ size 是一个很好的工具,它可以输出有关可执行文件代码大小 如您所见,我们在这里使用了 addArtifactArg,因为 addSystemCommand 只会返回一个 std.build.RunStep。这样,我们就可以增量构建完整的命令行,包括任何 LibExeObjStep 输出、FileSource 或逐字参数。 -# [全新工具]($section.id('全新工具')) +# [全新工具]($heading.id('new-tools')) 最酷的是 我们还可以从 LibExeObjStep 获取 std.build.RunStep: @@ -389,7 +389,7 @@ pub fn build(b: *std.build.Builder) void { 调用 zig build pack 时,我们将运行 tools/pack.zig。这很酷,因为我们还可以从头开始编译所需的工具。为了获得最佳的开发体验,你甚至可以从源代码编译像 bison 这样的 "外部 "工具,这样就不会依赖系统了! -# [将所有内容放在一起]($section.id('将所有内容放在一起')) +# [将所有内容放在一起]($heading.id('putting-everything-together')) 一开始,所有这些都会让人望而生畏,但如果我们看一个更大的 build.zig 实例,就会发现一个好的构建文件结构会给我们带来很大帮助。 @@ -491,7 +491,7 @@ pub fn build(b: *std.build.Builder) void { 两者都是为了在主机平台上运行,而不是在目标机器上。 此外,deploy_tool 还设置了固定的编译模式,因为我们希望快速编译,即使我们编译的是应用程序的调试版本。 -# [总结]($section.id('summary')) +# [总结]($heading.id('summary')) 看完这一大堆文字,你现在应该可以构建任何你想要的项目了。我们已经学会了如何编译 Zig 应用程序,如何为其添加任何类型的外部库,甚至如何为发布管理对应用程序进行后处理。 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 09e5d58..acf200a 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 @@ -14,7 +14,7 @@ You've just written an application in Zig and want others to use it. A convenient way for users to use your application is to provide a pre-built executable file. Next, I'll discuss the two main things that need to be handled correctly in a good release process. -# [Why provide pre-built executable files?]($section.id('Why provide pre-built executable files?')) +# [Why provide pre-built executable files?]($heading.id('why-provide-pre-built-executable-files')) Given how C/C++ dependencies work (or don't work), for some C/C++ projects, providing pre-compiled executable files is almost a necessity, @@ -26,7 +26,7 @@ That said, the more popular your application is, the less users will care what l Your users don't want to install Zig and run the build process to easily use your application (99% of the time, the rest 1% will be discussed later), so it's still better to pre-build your application. -# [`zig build` vs `zig build-exe`]($section.id('`zig build` vs `zig build-exe`')) +# [`zig build` vs `zig build-exe`]($heading.id('zig-build-vs-zig-build-exe')) In this article, we'll see how to make, release a build for a Zig project, so it's worth spending a little time to fully understand the relationship between Zig build system and command line. @@ -48,7 +48,7 @@ what `zig build` does is to prepare command line parameters for `build-exe`. This means that in compiling Zig code, `zig build` (assuming the code in `build.zig` is correct) and `zig build-exe` are one-to-one correspondence. The only difference is convenience. -# [Build mode]($section.id('Build mode')) +# [Build mode]($heading.id('build-mode')) When building a Zig project with `zig build` or `zig build-exe myapp.zig`, the default is a debug build executable file. Debug build is mainly for development convenience, so it's usually considered unsuitable for release. @@ -70,7 +70,7 @@ but it's not prioritized for performance, but rather for trying to minimize the For example, this is a very meaningful build mode for WebAssembly, because you want the executable file to be as small as possible, and the sandbox runtime environment has provided a lot of security guarantees. -# [How to set build mode]($section.id('How to set build mode')) +# [How to set build mode]($heading.id('how-to-set-build-mode')) When using `zig build-exe`, you can add `-O ReleaseSafe` (or `ReleaseFast`, or `ReleaseSmall`) to get the corresponding build mode. @@ -98,7 +98,7 @@ const exe = b.addExecutable(.{ This is how you specify release mode in command line: `zig build -Doptimize=ReleaseSafe` (or `-Doptimize=ReleaseFast`, or `-Doptimize=ReleaseSmall`). -# [Choose the correct build target]($section.id('Choose the correct build target')) +# [Choose the correct build target]($heading.id('choose-the-correct-build-target')) Now, we've chosen the correct release mode, it's time to consider build target. Obviously, if the platform used and build platform are different, the corresponding build target needs to be specified, @@ -109,7 +109,7 @@ The most straightforward way is to directly call `zig build` or `zig build-exe` If you do this, sometimes it works, but sometimes it crashes due to `illegal instruction` (or similar error). What's happening? -# [CPU features]($section.id('CPU features')) +# [CPU features]($heading.id('cpu-features')) If build target is not specified when building, Zig will build for the current machine, which means it will use all instruction sets supported by the current CPU. If the CPU supports AVX extension, @@ -173,7 +173,7 @@ const exe = b.addExecutable(.{ This also means if you want to add other restrictions or change how to specify target when building, you can achieve this by adding your own code. -# [Conclusion]($section.id('conclusion')) +# [Conclusion]($heading.id('conclusion')) Now you've learned the things you need to ensure correctly when releasing a build: choose a release optimization mode and choose the correct build target, including building for the same system you're building. diff --git a/content/post/2024-04-06-zig-cpp.smd b/content/post/2024-04-06-zig-cpp.smd index 7a9afe3..c6c1f23 100644 --- a/content/post/2024-04-06-zig-cpp.smd +++ b/content/post/2024-04-06-zig-cpp.smd @@ -8,7 +8,7 @@ 尽管 Zig 社区宣称 Zig 语言是一个更好的 C (better C),但是我个人在学习 Zig 语言时经常会"触类旁通"C++。在这里列举一些例子来说明我的一些体会,可能会有一些不正确的地方,欢迎批评指正。 -# ["元能力" vs "元类型"]($section.id('"元能力" vs "元类型"')) +# ["元能力" vs "元类型"]($heading.id('meta-ability-vs-meta-type')) 在我看来,C++的增强方式是希望赋予语言一种"元能力",能够让人重新发明新的类型,使得使用 C++的程序员使用自定义的类型,进行一种类似于"领域内语言"(DSL)编程。一个通常的说法就是 C++中任何类型定义都像是在模仿基本类型`int`。比如我们有一种类型 T,那么我们则需要定义 T 在以下几种使用场景的行为: @@ -26,7 +26,7 @@ x = y; //x的类型是T,复制运算符重载,当然也有可能是移动运 不过话说回来,很多底层系统的开发需求往往和这种类型系统的构建相悖,比如如果你的类型就是一个`int`的封装,那么即使发生拷贝你也无所谓性能开销。但是如果是一个`struct`,那么通常情况下,你会比较 care 拷贝,而可能考虑"移动"之类的手段。这个时候各种 C++的提供的幻觉,就成了程序员开发的绊脚石,经常你需要分析一段 C++表达式里到底有没有发生拷贝,他是左值还是右值,其实你在写 C 语言的时候也很少去考虑了这些,你在 Zig 里同样也不需要。 -# [类型系统]($section.id('类型系统')) +# [类型系统]($heading.id('type-system')) C 语言最大弊病就是没有提供标准库,C++的标准库你要是能看懂,得具备相当的 C++的语法知识,但是 Zig 的标准库几乎不需要文档就能看懂。这其实是因为,在 C++里,类型不是一等成员(first class member),因此实现一些模版元编程算法特别不直观。但是在 Zig 里,`type`就是一等成员,比如你可以写: @@ -63,7 +63,7 @@ Some::OutputType 相当于对于 InputType 调用一个 Some"函数",然后输出一个 OutputType。 -# [命令式 VS 声明式]($section.id('命令式 VS 声明式')) +# [命令式 VS 声明式]($heading.id('imperative-vs-declarative')) 比如实现一个函数,输入一个 bool 值,根据 bool 值,如果为真,那么输出 type A,如果为假那么输出 type B。 @@ -163,6 +163,6 @@ pub fn main() void { 即这个也是完全命令式的。当然 C++20 之后也出现了`if constexpr`和`concept`来进一步简化模版元编程,C++的元编程也在向命令式的方向进化。 -# [结束语]($section.id('conclusion')) +# [结束语]($heading.id('conclusion')) 尽管 Zig 目前"还不成熟",但是学习 Zig,如果采用一种对照的思路,偶尔也会"触类旁通"C++,达到举一反三的效果。 diff --git a/content/post/2024-05-07-package-hash.smd b/content/post/2024-05-07-package-hash.smd index 0267ce5..83f8064 100644 --- a/content/post/2024-05-07-package-hash.smd +++ b/content/post/2024-05-07-package-hash.smd @@ -6,11 +6,11 @@ .draft = false, --- -# [package hash]($section.id('package-hash')) +# [package hash]($heading.id('package-hash')) > 原文地址:[build.zig.zon dependency hashes](https://zig.news/michalsieron/buildzigzon-dependency-hashes-47kj) -# [引言]($section.id('引言')) +# [引言]($heading.id('introduction')) 作者 Michał Sieroń 最近在思考 `build.zig.zon` 中的依赖项哈希值的问题。这些哈希值都有相同的前缀,而这对加密哈希函数来说极其不同寻常。习惯性使用 Conda 和 Yocto 对下载的压缩包运行 sha256sum,但生成的摘要与 `build.zig.zon` 中的哈希值完全不同。 @@ -34,7 +34,7 @@ 以上摘取自 [hexops/mach](https://github.com/hexops/mach/blob/bffc66800584123e2844c4bc4b577940806f9088/build.zig.zon#L13-L26) 项目。 -# [初步探索]($section.id('初步探索')) +# [初步探索]($heading.id('preliminary-exploration')) 经过一番探索,我找到了一个文档:[doc/build.zig.zon.md](https://github.com/ziglang/zig/blob/1a6485d111d270014f57c46c53597a516a24dc47/doc/build.zig.zon.md),似乎没有任何线索指向它。而文档中对哈希有段简短的描述。 @@ -44,7 +44,7 @@ 该哈希值是基于一系列文件内容计算得出的,这些文件是在获取URL后并应用了路径规则后得到的。 这个字段是最重要的;一个包是的唯一性是由它的哈希值确定的,不同的 URL 可能对应同一个包。 -# [多重哈希]($section.id('多重哈希')) +# [多重哈希]($heading.id('multiple-hashing')) 在他们的网站上有一个很好的可视化展示,说明了这一过程: [多重哈希](https://multiformats.io/multihash/)。 @@ -52,7 +52,7 @@ 因此 `build.zig.zon` 中的哈希字段不仅包含了摘要(digest),还包含了一些元数据(metadata)。但即使我们丢弃了头部信息,得到的结果仍与下载的 `tar` 包的 `sha256` 摘要不相符。而这就涉及到了包含规则的问题。 -# [包含规则(inclusion rules)]($section.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) 文件,我们看到: @@ -93,7 +93,7 @@ pub fn includePath(self: Filter, sub_path: []const u8) bool { 除此之外,这个函数会检查 `sub_path` 是否被明确列出,或者是已明确列出的目录的子目录。 -# [计算哈希]($section.id('计算哈希')) +# [计算哈希]($heading.id('calculate-the-hash')) 现在我们知道了 `build.zig.zon` 的包含规则,也知道使用了 `SHA256` 算法。但我们仍然不知道实际的哈希结果是如何得到的。例如,它可能是通过将所有包含的文件内容输入哈希器来计算的。所以让我们再仔细看看,也许我们可以找到答案。 @@ -121,7 +121,7 @@ try all_files.append(hashed_file); 跟踪 `workerHashFile`,我们看到它是 `hashFileFallible` 的一个简单包装,而后者看起来相当复杂。让我们来分解一下。 -# [单个文件的哈希计算]($section.id('单个文件的哈希计算')) +# [单个文件的哈希计算]($heading.id('hash-calculation-for-individual-files')) 首先,会进行一些初始化设置,其中创建并用规整后的文件路径初始化了一个新的哈希器实例: @@ -169,7 +169,7 @@ hasher.update(link_name); 首先进行路径分隔符的规整,保证不同平台一致,之后将符号链接的目标路径输入 `hasher`。在 `hashFileFallible` 函数最后,把计算出的哈希值赋值给 `HashedFile` 对象的 `hash` 字段。 -# [组合哈希]($section.id('组合哈希')) +# [组合哈希]($heading.id('combined-hash')) 尽管有了单个文件的哈希值,但我们仍不知道如何得到最终的哈希。幸运的是,曙光就在眼前。 @@ -199,13 +199,13 @@ for (all_files.items) |hashed_file| { 在这里我们看到所有计算出的哈希被一个接一个地输入到一个新的哈希器中。在 `computeHash` 的最后,我们返回 `hasher.finalResult()`,现在我们明白哈希值是如何获得的了。 -# [最终多哈希值]($section.id('最终多哈希值')) +# [最终多哈希值]($heading.id('final-multiple-hash-value')) 现在我们有了一个 `SHA256` 摘要,可以最终返回到 `main.zig`,在那里我们调用 [`Package.Manifest.hexDigest(fetch.actual_hash)`](https://github.com/ziglang/zig/blob/9d64332a5959b4955fe1a1eac793b48932b4a8a8/src/Package/Manifest.zig#L174)。在那里,我们将多哈希头写入缓冲区,之后是我们的组合摘要。 顺便说一下,我们看到所有哈希头都是 `1220` 并非巧合。这是因为 `Zig` [硬编码了 SHA256](https://github.com/ziglang/zig/blob/1a6485d111d270014f57c46c53597a516a24dc47/src/Package/Manifest.zig#L3) - 0x12,它有 32 字节的摘要 - 0x20。 -# [总结]($section.id('总结')) +# [总结]($heading.id('summary')) 总结一下:最终哈希值是一个多哈希头 + `SHA256` 摘要。 @@ -215,7 +215,7 @@ for (all_files.items) |hashed_file| { 在实验这个之后,我有一个想法,我很惊讶 Zig 没有检查 `build.zig.zon` 中列出的所有文件是否存在。但这可能是另一天的话题了。 -# [译者注]($section.id('translators-note')) +# [译者注]($heading.id('translators-note')) 在使用本地包时,可以使用下面的命令进行 hash 问题的排查: diff --git a/content/post/2024-05-24-interface-idioms.smd b/content/post/2024-05-24-interface-idioms.smd index 916fd21..776e275 100644 --- a/content/post/2024-05-24-interface-idioms.smd +++ b/content/post/2024-05-24-interface-idioms.smd @@ -8,7 +8,7 @@ > 原文链接: https://zig.news/yglcode/code-study-interface-idiomspatterns-in-zig-standard-libraries-4lkj -# [引言]($section.id('引言')) +# [引言]($heading.id('introduction')) 在 Java 和 Go 中,可以使用“接口”(一组方法或方法集)定义基于行为的抽象。通常接口包含所谓的虚表(`vtable`) 以实现动态分派。Zig 允许在结构体、枚举、联合和不透明类型中声明函数和方法,尽管 Zig 尚未支持接口作为一种语言特性。 @@ -31,7 +31,7 @@ Zig 标准库应用了一些代码习语或模式以达到类似效果。 完整代码位于[此仓库](https://github.com/yglcode/zig_interfaces),你可以使用 `zig test interfaces.zig` 运行它。 -# [背景设定]($section.id('背景设定')) +# [背景设定]($heading.id('background-setting')) 让我们使用经典的面向对象编程示例,创建一些形状:点(`Point`)、盒子(`Box`)和圆(`Circle`)。 @@ -86,7 +86,7 @@ fn init_data() struct { point: Point, box: Box, circle: Circle } { } ``` -# [接口1:枚举标签联合]($section.id('接口1:枚举标签联合')) +# [接口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 和动态分派的第一种实现]($section.id('接口2:vtable 和动态分派的第一种实现')) +# [接口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)。 @@ -198,7 +198,7 @@ test "vtab1_as_intf" { } ``` -# [接口3:vtable 和动态分派的第二种实现]($section.id('接口3:vtable 和动态分派的第二种实现')) +# [接口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() 的原始动态分派]($section.id('接口4:使用嵌入式 vtab 和 @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:编译时的泛型接口]($section.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 ba84b9f..505d53b 100644 --- a/content/post/2024-06-10-zig-hashmap-1.smd +++ b/content/post/2024-06-10-zig-hashmap-1.smd @@ -24,7 +24,7 @@ pub fn put(self: *Self, key: K, value: V) Allocator.Error!void { 正如我所说,大部分繁重的工作都由 `std.HashMapUnmanaged` 完成,其他变体通过一个名为 `unmanaged` 的字段对其进行封装。 -# [Unmanaged]($section.id('Unmanaged')) +# [Unmanaged]($heading.id('unmanaged')) 在Zig标准库中随处可见的类型命名约定是 `unmanaged`。这种命名方式表明所涉及的类型不维护 `allocator`。任何需要分配内存的方法都会显式地将 `allocator` 作为参数传递。要实际看到这一点,可以考虑下面这个链表的例子: @@ -143,7 +143,7 @@ pub fn LinkedList(comptime T: type) type { 为了简化,本文的其余部分不会再提到 `unmanaged`。我们看到关于 `StringHashMap` 或 `AutoHashMap` 或 `HashMap` 的任何内容同样适用于它们的 Unmanaged 变体。 -# [HashMap 与 AutoHashMap]($section.id('HashMap 与 AutoHashMap')) +# [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)`。 @@ -234,7 +234,7 @@ pub const StringContext = struct { }; ``` -# [自定义上下文]($section.id('自定义上下文')) +# [自定义上下文]($heading.id('custom-context')) 我们将在第一部分结束时,直接使用 `HashMap`,这意味着提供我们自己的上下文。我们将从一个简单的例子开始:为不区分大小写的 ASCII 字符串创建一个 `HashMap`。我们希望以下内容输出:`Goku = 9000`。请注意,虽然我们使用键 `GOKU` 进行插入,但我们使用“goku”进行获取: @@ -379,7 +379,7 @@ pub fn hash(_: HashContext, u: User) u64 { 插入这两个函数,以上示例应该可以工作。 -# [结论]($section.id('conclusion')) +# [结论]($heading.id('conclusion')) 希望你现在对 Zig 的哈希表的实现以及如何在代码中利用它们有了更好的理解。在大多数情况下,`std.StringHashMap` 或 `std.AutoHashMap` 就足够了。但知道 `*Unmanaged` 变体的存在和目的,以及更通用的 `std.HashMap`,可能会派上用场。如果没有其他用途,现在文档和它们的实现应该更容易理解了。 diff --git a/content/post/2024-06-11-zig-hashmap-2.smd b/content/post/2024-06-11-zig-hashmap-2.smd index dac855e..0dbf329 100644 --- a/content/post/2024-06-11-zig-hashmap-2.smd +++ b/content/post/2024-06-11-zig-hashmap-2.smd @@ -41,7 +41,7 @@ keys: values: 这个基本的可视化表示将贯穿本文大部分内容,并且不断强调条目的位置需要保持一致性和可预测性。即使哈希表需要在增长时从一个底层数组移动到另一个(即当填充因子达到一定阈值并要求扩大以容纳更多数据时),这一事实是我们将反复回顾的主题。 -# [值管理]($section.id('值管理')) +# [值管理]($heading.id('value-management')) 如果我们对上述代码片段进行扩展,并调用 `lookup.get("Paul")`,返回的值将是 `1234`。在处理像 `i32` 这样的原始类型时,很难直观地理解 `get` 方法和它的可选返回类型 `?i32` 或更通用的 `?V`(其中 `V` 表示任何值类型)之间的区别。考虑到这一点,让我们通过替换 `i32` 为一个封装了更多信息的 `User` 类型来展示这一概念: @@ -224,7 +224,7 @@ while (it.next()) |value_ptr| { 在最后一种情况下,由于我们存储的是 `User` 而不是 `*User`,我们的 `value_ptr` 是指向 `User` 的指针(不像之前那样是指向指针的指针)。 -# [Keys]($section.id('Keys')) +# [Keys]($heading.id('keys')) 我们可以开始和结束这一节:我们关于值的所有内容同样适用于键。这是100%正确的,但这在某种程度上不太直观。大多数开发人员很快就能理解,存储在哈希表中的堆分配的 `User` 实例有其自身的生命周期,需要显式管理/释放。但由于某些原因,这对于键来说并不那么明显。 @@ -337,7 +337,7 @@ if (lookup.fetchRemove(user.name)) |kv| { 对于大多数情况,在处理非原始键或值时,关键是当你调用哈希表的 `deinit` 时,你为键和值分配的任何内存不会被自动释放;你需要自己处理。 -# [getOrPut]($section.id('getOrPut')) +# [getOrPut]($heading.id('getorput')) 虽然我们已经讨论过的内容有很多含义,但对我来说,直接暴露键和值指针的最大好处之一是 `getOrPut` 方法。 @@ -367,7 +367,7 @@ if (gop.found_existing) { 当然,只要不对哈希表进行修改,`value_ptr` 就应被视为有效。顺便提一句,这同样适用于我们通过 `iterator()`、`valueIterator` 和 `keyIterator` 获取的迭代键和值,原因相同。 -# [结论]($section.id('conclusion')) +# [结论]($heading.id('conclusion')) 希望你现在对使用「std.HashMap」、「std.AutoHashMap」和「std.StringHashMap」以及它们的「unmanaged」变体感到更加得心应手。虽然你可能永远不需要提供自己的上下文(例如「hash」和「eql」函数),但了解这是一个选项是有益的。在日常编程中,可视化数据尤其有用,尤其是在使用指针和添加间接层次时。每当我处理 `value_ptr` 或 `key_ptr` 时,我都会想到这些切片以及值或键与这些切片中值或键的实际地址之间的区别。 diff --git a/content/post/2024-08-12-zoop.smd b/content/post/2024-08-12-zoop.smd index fe939ce..4f068f9 100644 --- a/content/post/2024-08-12-zoop.smd +++ b/content/post/2024-08-12-zoop.smd @@ -6,17 +6,17 @@ .draft = false, --- -# [zoop]($section.id('zoop')) +# [zoop]($heading.id('zoop')) zoop 是 zig 的一个 OOP 解决方案,详细信息可以看看 [zoop官网](https://zhuyadong.github.io/zoop-docs/)。 -# [为什么不用别的 OOP 语言]($section.id('为什么不用别的 OOP 语言')) +# [为什么不用别的 OOP 语言]($heading.id('why-not-use-another-oop-language')) 简单的说,是我个人原因,必需使用 zig 的同时,还一定要用 OOP,所以有了 zoop。 -# [zoop 入门]($section.id('zoop 入门')) +# [zoop 入门]($heading.id('getting-started-with-zoop')) -# [类和方法]($section.id('类和方法')) +# [类和方法]($heading.id('classes-and-methods')) ```zig pub const Base = struct { @@ -73,7 +73,7 @@ pub const Base = struct { 上面的代码给 `Base` 添加了一个可以继承的方法 `getName()`。 -# [类的继承]($section.id('类的继承')) +# [类的继承]($heading.id('class-inheritance')) zoop 引入一个关键字 `extends` 用来实现继承,比如下面我们定义 `Base` 的子类 `Child`: @@ -102,7 +102,7 @@ test { } ``` -# [接口定义]($section.id('接口定义')) +# [接口定义]($heading.id('interface-definition')) zoop 中的接口,实际上是一个胖指针。下面我们定义一个接口 `IGetName`: @@ -132,7 +132,7 @@ pub const IGetName = struct { 上面的代码具体原理下面会说到,这里大家知道接口就是这样定义的就行了。上面的代码定义了接口 `IGetName`,这个接口有一个方法 `getName()`。 -# [接口实现]($section.id('接口实现')) +# [接口实现]($heading.id('interface-implementation')) 上面的 `Base` 类正好也有个符合 `IGetName` 接口的方法 `getName()`,那我们修改一下 `Base` 的代码让它来实现 `IGetName` 接口: @@ -148,7 +148,7 @@ pub const Base = struct { 可以看到实现接口和继承用的同样一个关键字 `extends`。因为子类会继承父类的接口,所以这样一来,`Child` 也自动实现了 `IGetName` 接口。 -# [方法重写和虚函数调用]($section.id('方法重写和虚函数调用')) +# [方法重写和虚函数调用]($heading.id('method-overrides-and-virtual-function-calls')) 我们修改上面 `Child` 的代码,重写 `getName()` 方法: @@ -194,7 +194,7 @@ try t.expectEqualStrings(base.as(IGetName).?.getName(), "override"); 那么 zoop 的基本使用方法就介绍到这里,下面我们开始介绍 zoop 的实现原理。 -# [预设场景]($section.id('预设场景')) +# [预设场景]($heading.id('preset-scene')) 接下来的讨论基于如下的属于 `mymod` 模块的类和接口: @@ -285,7 +285,7 @@ pub const Child = struct { - `Base`: 基类,实现接口 `ISetName` - `Child`: 子类,继承 `Base`,并实现接口 `IGetName` -# [核心数据结构 `zoop.Mixin(T)`]($section.id('核心数据结构 `zoop.Mixin(T)`')) +# [核心数据结构 `zoop.Mixin(T)`]($heading.id('core-data-structure-zoopmixint')) 我们看看两个类的 `mixin` 这个数据里面有什么: @@ -339,7 +339,7 @@ zoop.Mixin(Child) = struct { 上面两个函数获取的都是最外层对象的数据。根据对 `mixin` 数据的分析,zoop 的类型转换的原理就很清楚了,大家可以参考官网上关于 [类型转换](https://zhuyadong.github.io/zoop-docs/guide/as-cast) 的内容。 -# [动态构造类的方法、接口方法、和 `Vtable`]($section.id('动态构造类的方法、接口方法、和 `Vtable`')) +# [动态构造类的方法、接口方法、和 `Vtable`]($heading.id('dynamically-constructing-class-methods-interface-methods-and-vtable')) OOP 概念中的继承,重写,虚函数,实质其实就是在编译时动态构造需要的方法和属性。zoop 中主要是通过通过 [zoop.tuple](https://zhuyadong.github.io/zoop-docs/reference/tuple) 这个模块来进行编译时动态构造。 @@ -347,11 +347,11 @@ OOP 概念中的继承,重写,虚函数,实质其实就是在编译时动 下面我介绍一下 zoop 中用到的 `comptime` 一些技巧,相信会对大家今后使用 zig 有帮助。 -# [`struct` 很万能]($section.id('`struct` 很万能')) +# [`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) 就是通过这个方法实现的。 -# [动态构造 `struct` 的字段,用 `@Type()`]($section.id('动态构造 `struct` 的字段,用 `@Type()`')) +# [动态构造 `struct` 的字段,用 `@Type()`]($heading.id('dynamically-construct-the-fields-of-struct-using-type')) 网上好像很少有关于 `@Type()` 的使用说明,一般都是通过看 `zig.std` 的代码来学习,那我这里就稍微说明一下,希望能对大家有帮助。 目前 zig 通过 `@Type()`,能动态构造的 `struct`,只有纯字段类型的 `struct` (个人理解)。构造的方法,就是先把计算好的一个 `std.builtin.Type.StructField` 数组传递给 `@Type()` 来返回一个 `struct`,比如以下代码: @@ -398,7 +398,7 @@ const MyStruct = struct { zoop 动态构造 `Vtable` 就是通过这个方法做到的,参考 [zoop.DefVtable 原理](https://zhuyadong.github.io/zoop-docs/reference/principle#DefVtable) 和 [zoop 源代码](https://github.com/zhuyadong/zoop.git) -# [动态构造 `struct` 的函数,用 `usingnamespace`]($section.id('动态构造 `struct` 的函数,用 `usingnamespace`')) +# [动态构造 `struct` 的函数,用 `usingnamespace`]($heading.id('dynamically-construct-the-function-of-struct-using-usingnamespace')) 要想定义 `struct` 中的函数,理论上代码是一定要写在 `struct` 中的,目前 zig 唯一留下的一个口子,就是 `usingnamespace`,zoop 正是利用这个特性,来动态构造 `struct` 的函数。 @@ -442,7 +442,7 @@ pub usingnamespace struct { 因而实现了对 `Base.setName()` 方法的继承。 -# [运行时根据类型找 `Vtable` 和父类指针]($section.id('finding-vtable-and-parent-class-pointers-at-runtime-based-on-type')) +# [运行时根据类型找 `Vtable` 和父类指针]($heading.id('finding-vtable-and-parent-class-pointers-at-runtime-based-on-type')) 这个功能的实现当时第一版是使用的 `std.StaticStringMap` 保存了一个类中所有接口名到接口 `Vtable` ,以及父类名到父类数据在本类中的地址偏移的映射。和 C++ 的 `dynamic_cast` 比起来,性能是比较差的。后来看到西瓜大大发的一个链接 [点这里](https://github.com/SuperAuguste/cursed-zig-errors),忽然意识到这不就是我一直想要的 `comptime` 全局变量么,我终于能写出 `typeId(comptime T: type) u32` 这样的函数了: diff --git a/content/post/2024-11-26-typed-fsm.smd b/content/post/2024-11-26-typed-fsm.smd index 8ae6d63..1c49937 100644 --- a/content/post/2024-11-26-typed-fsm.smd +++ b/content/post/2024-11-26-typed-fsm.smd @@ -6,11 +6,11 @@ .draft = false, --- -# [typed fsm]($section.id('typed-fsm')) +# [typed fsm]($heading.id('typed-fsm')) -# [1. 简单介绍类型化有限状态机的优势]($section.id('1. 简单介绍类型化有限状态机的优势')) +# [1. 简单介绍类型化有限状态机的优势]($heading.id('1-briefly-introduce-the-advantages-of-typed-finite-state-machines')) -# [1.1 介绍有限状态机]($section.id('1.1 介绍有限状态机')) +# [1.1 介绍有限状态机]($heading.id('11-introduction-to-finite-state-machines')) 有限状态机(FSM,以下简称状态机)是程序中很常见的设计模式。 @@ -39,13 +39,13 @@ typed-fsm-zig 是一个利用 zig 类型系统加一些编程规范实现的一 在实际的使用中没有任何的代码生成,除了一处隐式的约束要求之外,没有任何其它的控制,开发者完全掌握状态机,因此你可以方便的将它和你现有的代码结合起来。 -# [2. 例子:修改 ATM 状态机的状态]($section.id('2. 例子:修改 ATM 状态机的状态')) +# [2. 例子:修改 ATM 状态机的状态]($heading.id('2-example-modifying-the-state-of-an-atm-state-machine')) 这里我将以一个 ATM 状态机(以下简称 ATM)的例子来展示 typed-fsm-zig 和 zig 的类型系统如何帮助我快速修改 ATM 的状态。 为了简单性,这里我不展示构建 ATM 这个例子的过程,感兴趣的可以在这里看到[代码](https://github.com/sdzx-1/typed-fsm-zig/blob/master/examples/atm-gui.zig)。 -# [2.1 介绍 ATM 状态机]($section.id('2.1 介绍 ATM 状态机')) +# [2.1 介绍 ATM 状态机]($heading.id('21-introducing-atm-state-machines')) ATM 代表自动取款机,因此它的代码的逻辑就是模拟自动取款机的一些行为:插入银行卡,输入 pin,检查 pin,取钱,修改 pin。 @@ -76,7 +76,7 @@ ATM 代表自动取款机,因此它的代码的逻辑就是模拟自动取款 接下来的文章中我将修改 Update 的行为,并展示在这个过程中类型系统如何帮助我快速调整代码。 -# [2.2 修改 Update 消息]($section.id('2.2 修改 Update 消息')) +# [2.2 修改 Update 消息]($heading.id('22-modify-update-messages')) 实际的消息 Update 定义代码如下 @@ -134,7 +134,7 @@ referenced by: 在这里类型系统精确的告诉了我们需要修改的地方,以及原因。修改完成后程序即能正确运行。 -# [2.3 移除 changePin 状态]($section.id('2.3 移除 changePin 状态')) +# [2.3 移除 changePin 状态]($heading.id('23-remove-changepin-status')) 这一节中我们尝试移除 changePin 状态,看看类型系统会给我们什么反馈。 如果移除 changePin,新的状态图如下: @@ -177,7 +177,7 @@ examples/atm-gui.zig:296:10: error: no field named 'ChangePin' in enum '@typeInf 在这个过程中类型系统帮助我们找到问题和原因。这非常酷!!! -# [2.4 总结]($section.id('2.4 总结')) +# [2.4 总结]($heading.id('24-summary')) 以上是一个简单的例子,展示了 typed-fsm-zig 对于提升状态机编程体验的巨大效果。 @@ -194,7 +194,7 @@ examples/atm-gui.zig:296:10: error: no field named 'ChangePin' in enum '@typeInf -# [3. 原理与实现]($section.id('3. 原理与实现')) +# [3. 原理与实现]($heading.id('3-principle-and-implementation')) 最开始的版本是[typed-fsm](https://github.com/sdzx-1/typed-fsm),由使用 haskell 实现,它实现了完整类型安全的有限状态机。 @@ -434,7 +434,7 @@ fn s2Handler(val: Witness(Exmaple, .exit, .s2), ref: *i32) void { 以上就是 typed-fsm-zig 核心想法的完整介绍。接下来我将介绍需要的编程规范。 -# [4. typed-fsm-zig 需要哪些编程规范]($section.id('4. typed-fsm-zig 需要哪些编程规范')) +# [4. typed-fsm-zig 需要哪些编程规范]($heading.id('4-what-programming-specifications-do-typed-fsm-zig-need')) 1. 状态和消息集合之间需要满足的隐式命名规范 @@ -548,7 +548,7 @@ pub fn readyHandler(comptime w: AtmSt.EWitness(.ready), ist: *InternalState) voi 遵循这四点要求,就能获得强大的类型安全保证,足以让你愉快的使用状态机! -# [5. 接下来能够增强的功能]($section.id('5-future-enhancements')) +# [5. 接下来能够增强的功能]($heading.id('5-future-enhancements')) 暂时我能想到的有如下几点: diff --git a/content/post/2025-01-23-bonkers-comptime.smd b/content/post/2025-01-23-bonkers-comptime.smd index 24658b3..f78ac5b 100644 --- a/content/post/2025-01-23-bonkers-comptime.smd +++ b/content/post/2025-01-23-bonkers-comptime.smd @@ -10,7 +10,7 @@ > 译注:原文中的代码块是交互式,翻译时并没有移植。另外,由于 comptime 本身即是关键概念,并且下文的意思更侧重于 Zig comptime 的特性,故下文大多使用 comptime 代替编译时概念。 -# [引子]($section.id('引子')) +# [引子]($heading.id('introduction')) 编程通过自动化地处理数据极大地提升了生产力。而元编程则让我们可以像处理数据一样处理代码,以此将编程的力量反向作用于编程自身。而在底层编程中,我想元编程可能带来最大的优势,因为那些高级概念必须得精确映射到某些低级操作。然而,除了函数式编程语言外,我一直觉得各编程语言对元编程的实现并不理想。因此,当我看到 Zig 把元编程列为一个主要特性时,我提起了很大的兴趣。 @@ -20,7 +20,7 @@ 为了明确起见,所有示例都是有效的 Zig 代码,但示例中的转换只是概念性的,它们并不是 Zig 实际的实现方式。 -# [视角0: 忽略它]($section.id('视角0: 忽略它')) +# [视角0: 忽略它]($heading.id('perspective-0-ignore-it')) 我说我喜欢这个特性,却又立刻叫你忽略它,这确实有点怪。但我认为此处正是 Zig comptime 威力所体现的地方,所以我将从这里出发。Zig Zen 中的第三条是“倾向于阅读代码,而不是编写代码。”确实,能够轻松地阅读代码在各种情况下都很重要,因为它是建立概念理解的基础,而这种理解也是调试或修改代码所必需的。 @@ -71,7 +71,7 @@ pub fn main() void { Zig 中有很多基于 comptime 且远远不止这样简单的类型反射,但你只需要阅读那些代码、完全无需深入了解其中有关 comptime 的细节就可以理解它们在干什么。当然,如果你想使用 comptime 编写代码,则不能仅仅止步于此,让我们继续深入。 -# [视角1: 泛型]($section.id('视角1: 泛型')) +# [视角1: 泛型]($heading.id('perspective-1-generics')) 泛型在 Zig 中并不是一个特定的功能。相反,Zig 中的仅仅一小部分的 comptime 特性就可以提供用来处理你进行泛型编程所需的一切。这种视角虽然不能让你完全理解 comptime,但它确实为你提供了一个入口点,借此,你可以完成基于元编程的许多任务。 @@ -121,7 +121,7 @@ pub fn main() void { 当然,也可以通过使用特殊类型 anytype 来推断参数的类型,而这通常在参数的类型对函数签名的其余部分没有影响时使用。(译注:此时要限制 a, b, c 的类型相同,所以此处不用 anytype ) -# [视角2:编译时运行的标准代码]($section.id('视角2:编译时运行的标准代码')) +# [视角2:编译时运行的标准代码]($heading.id('perspective-2-standard-code-running-at-compile-time')) 这是一个古老的故事: 增加一种自动执行命令的方法。当然,你还需要变量。 哦,还有条件。 拜托,能给我循环吗?这些看似合理的需求,最终导致这些自动化命令变得越来越复杂,甚至演变成一个完整的宏语言。 但 Zig 不同, 在运行时、编译时,甚至是构建系统中都使用了相同的语言。 @@ -255,7 +255,7 @@ onst MyStruct = struct { 上面的示例是我们手动展开后的示例,但这项工作是由 Zig 的 comptime 完成的。这使得我们可以直接独立而完整地编写出我们要实现的功能,而不需要添加"当你改变 `MyStruct` 的字段时,记得更新 sum 函数"这样的由于依赖于 `MyStruct` 具体字段而预防功能失效的注释。 基于 comptime 的版本在 `MyStruct` 的任何字段变更时都可以正确地自动处理。 -# [视角4:Comptime 求值,运行时代码生成]($section.id('视角4:Comptime 求值,运行时代码生成')) +# [视角4:Comptime 求值,运行时代码生成]($heading.id('perspective-4-comptime-evaluation-runtime-code-generation')) 这与程序特化(Partial Evaluation)非常相似。这里有两个版本的代码,输入(编译前)和输出(编译后)。输入代码由编译器运行。如果一个语句在编译时是可知的,它就会被直接求值。但是如果一个语句需要某些运行时的值,那么这个语句就会被添加到输出代码中。 @@ -305,7 +305,7 @@ const MyStruct = struct { 这样做的另一个后果是,Zig 代码的静态分析要比大多数静态类型语言复杂得多,因为编译器需要运行很大一部分才能确定所有类型。 因此,在 Zig 工具链跟上之前,代码自动补全等编辑工具并不总是能很好地发挥作用。 -# [视角5:直接生成代码(Textual Code Generation)]($section.id('视角5:直接生成代码(Textual Code Generation)')) +# [视角5:直接生成代码(Textual Code Generation)]($heading.id('perspective-5-direct-code-generation')) 我在文章开头感叹元编程难度。然而,即使在 Zig 中,它仍然是一个强大的工具,在解决某些问题方面也占有一席之地。如果您熟悉这种元编程方法,对 Zig comptime 提供的功能可能会觉得有些残缺。比如, 怎么在写一段代码在运行时能够生成新代码? @@ -368,13 +368,13 @@ pub fn writeMyStructOfType( 与本节相关的是文本宏,如 C 语言中的文本宏。你可以做的大多数正常事情都可以在 comptime 中完成,尽管它们很少采用类似的形式。 不过,文本宏并不能做所有允许做的事情。 例如,你不能决定不喜欢某个 Zig 关键字,然后让宏代替你自己的关键字。 我认为这是一个正确的决定,尽管对于那些习惯了这种能力的人来说,这是一个艰难的过渡。 此外,Zig 参考了半个世纪以来的程序员在这方面的探索,所以它的选择要理智得多。 -# [结论]($section.id('结论')) +# [结论]($heading.id('conclusion')) 在阅读 Zig 代码以理解代码行为时,考虑 comptime 并不是必要的。而当编写 comptime 代码时,我通常会将其视为程序特化(Partial Evaluation)的一种形式。然而,如果你知道如何使用不同的元编程方法解决问题,你很可能有能力将其翻译成 comptime 形式。 元编程中直接生成代码的方法的存在,就是我全力支持 Zig 风格的 comptime 元编程的原因。尽管,直接生成代码是几乎是最强大的,但是,在阅读和调试时忽略 comptime 的特性的元编程方法确是最简单的。正因如此,我给本文取名为《Zig comptime 棒极了》。 -# [进一步阅读]($section.id('further-reading')) +# [进一步阅读]($heading.id('further-reading')) Zig 并非一个仅仅依赖 comptime 这一特性的语言。你可以在[官方网站](https://ziglang.org/)上了解更多关于 Zig 的信息。 diff --git a/content/post/2025-07-19-zine-migration.smd b/content/post/2025-07-19-zine-migration.smd index 6f44305..c319ed4 100644 --- a/content/post/2025-07-19-zine-migration.smd +++ b/content/post/2025-07-19-zine-migration.smd @@ -7,7 +7,7 @@ 本文复盘了 ZigCC 社区网站从 Hugo 迁移至 Zine 的全过程,旨在分享其中的经验与思考,为有类似需求的朋友提供参考。 -# [一、为何选择 Zine?]($section.id("为何选择-Zine")) +# [一、为何选择 Zine?]($heading.id('why-choose-zine')) 我们最初使用 Hugo 及其 Docsy 主题搭建网站,但在使用过程中遇到了一些痛点,促使我们寻找新的解决方案。选择迁移至 Zine 主要基于以下几点考量: @@ -22,11 +22,11 @@ 作为 Zig 社区的一份子,我们有责任参与到生态建设中。果不其然,这次迁移过程也让我们发现了 Zine 的一些待解决问题和潜在的改进点。 -# [二、Zine 核心概念解读]($section.id("Zine核心概念解读")) +# [二、Zine 核心概念解读]($heading.id('interpretation-of-zines-core-concepts')) Zine 是一个现代、高效的静态网站生成器。要理解它,首先要掌握其核心设计理念和文件结构。 -## [项目结构与配置]($section.id("项目结构与配置")) +## [项目结构与配置]($heading.id('project-structure-and-configuration')) 一个基础的 Zine 项目结构与 Hugo 等主流生成器类似,主要包含内容、布局和静态资源目录: @@ -56,7 +56,7 @@ Site { } ``` -## [内容渲染:SuperMD、SuperHTML 与 Scripty]($section.id("内容渲染")) +## [内容渲染:SuperMD、SuperHTML 与 Scripty]($heading.id('content-rendering')) Zine 在内容渲染上采用了与 Hugo 截然不同的哲学。它没有直接使用标准的 Markdown 和 HTML,而是引入了三个核心概念: @@ -96,11 +96,11 @@ Scripty 作为表达式语言,其威力在条件和嵌套逻辑中得以体现 通过这三者的结合,Zine 在保证内容与布局分离的同时,提供了高度的灵活性和安全性。更详细的用法,推荐阅读 Zine 官方文档。 -# [三、迁移步骤详解]($section.id("迁移步骤详解")) +# [三、迁移步骤详解]($heading.id('detailed-explanation-of-migration-steps')) 整个迁移过程可以分为内容转换、布局设计、预览调试和最终部署几个关键步骤。 -## [内容格式转换]($section.id("内容格式转换")) +## [内容格式转换]($heading.id('content-format-conversion')) 迁移的核心工作是将原有内容(本文中主要是 Markdown 和 Org Mode 文件)转换为 Zine 支持的 SuperMarkdown (`.smd`) 格式。 @@ -121,12 +121,12 @@ end Pandoc 完成初步转换后,仍需手动调整以适配 `.smd` 和 `.shtml` 的专属语法。常见差异包括: 1. **HTML 嵌入**:`.smd` 不推荐直接嵌入 HTML。相关代码需要用 ` ```=html` 代码块包裹。 -2. **章节标题链接**:标题不会自动生成 ID 和锚点链接,需要手动使用 `$section.id("custom-id")` 添加(注意 ID 不能包含空格)。 +2. **章节标题链接**:标题不会自动生成 ID 和锚点链接,需要手动使用 `$heading.id("custom-id")` 添加(注意 ID 不能包含空格)。 3. **资源引用**:可以使用 Scripty API 或将资源放在 `public` 目录下通过绝对路径 (`/path/to/resource`) 引用。 建议先完成布局文件的编写,在实时预览的环境下再进行这些细致的手动适配,效率更高。 -## [处理 Frontmatter]($section.id("处理-Frontmatter")) +## [处理 Frontmatter]($heading.id('processing-frontmatter')) Zine 使用 Ziggy 语法作为 Frontmatter 的格式,它与 Zig 语法高度相似。 @@ -144,7 +144,7 @@ Zine 使用 Ziggy 语法作为 Frontmatter 的格式,它与 Zig 语法高度 --- ``` -## [设计布局]($section.id("设计布局")) +## [设计布局]($heading.id('design-layout')) 由于我们希望采用全新的简约风格,因此我没有沿用 Docsy 的样式,而是重新设计了一套布局。目前的版本虽已上线,但在目录(TOC)等细节上仍有优化空间。 @@ -188,7 +188,7 @@ Zine 布局有几个关键特性: ``` -## [预览与调试]($section.id("预览与调试")) +## [预览与调试]($heading.id('preview-and-debugging')) 完成布局和初步内容转换后,就进入了最繁琐的环节:预览、检查和修正。 @@ -196,7 +196,7 @@ Zine 布局有几个关键特性: 这一步需要极大的耐心,几乎等同于重新校对所有文章。需要注意的是,Zine 目前仍在快速发展中,部分功能的报错信息可能不够明确,需要结合文档和实践耐心排查。 -## [部署]($section.id("部署")) +## [部署]($heading.id('deployment')) Zine 官方文档提供了详细的部署指南: @@ -205,7 +205,7 @@ Zine 官方文档提供了详细的部署指南: 你可以参考 zine-ssg 或本站(ZigCC)仓库中的 GitHub Actions 配置来设置自己的自动化部署流程。 -# [四、总结与经验分享]($section.id("总结与经验分享")) +# [四、总结与经验分享]($heading.id('summary-and-experience-sharing')) 最后,分享几点额外的经验: diff --git a/content/post/news/2023-12-11-first-meetup.smd b/content/post/news/2023-12-11-first-meetup.smd index ad8f810..3206a4d 100644 --- a/content/post/news/2023-12-11-first-meetup.smd +++ b/content/post/news/2023-12-11-first-meetup.smd @@ -45,7 +45,7 @@ Zig,目前主要有以下几个: 我们希望通过这些努力,提高 Zig 语言的知名度,完善 Zig 语言的生态,促进 Zig 语言的交流和学习。 -# [结论]($section.id('conclusion')) +# [结论]($heading.id('conclusion')) 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 ddcce3e..a19d4c5 100644 --- a/content/post/news/2023-12-27-second-meetup.smd +++ b/content/post/news/2023-12-27-second-meetup.smd @@ -18,7 +18,7 @@ 这次会议主要是同步了之前会议落实的 action,主要是同步了不同项目的进展,由于临近年底,大家进度都不算太大,但还是有所进展,算是开了个好头😄 -# [项目进展]($section.id('项目进展')) +# [项目进展]($heading.id('project-progress')) # [Zig-OS](https://github.com/zigcc/zig-os) @@ -33,7 +33,7 @@ action,主要是同步了不同项目的进展,由于临近年底,大家 - 增加了评论区的功能 - 待完成:反射(编译期反射和运行时反射)、内建函数说明(包含使用例子)、未定义行为、wasm、原子操作这些边缘部分 -# [Zig 教学视频]($section.id('Zig 教学视频')) +# [Zig 教学视频]($heading.id('zig-teaching-video')) - 主要参与人员:Lambert - @@ -44,14 +44,14 @@ action,主要是同步了不同项目的进展,由于临近年底,大家 - 主要参与人员:夜白、西瓜 - 已经完成大部分内容 👍 -# [Zig 构建系统教程]($section.id('Zig 构建系统教程')) +# [Zig 构建系统教程]($heading.id('zig-building-system-tutorial')) - 主要参与人员:Reco - 目前主要是对 [zig build explained](https://zig.news/xq/zig-build-explained-part-3-1ima) 系列文章翻译 -# [新人介绍]($section.id('new-member-introduction')) +# [新人介绍]($heading.id('new-member-introduction')) 在第一次会议后,有一些朋友想加入 ZigCC 社区,经过简单筛选,新增一名成员:Reco,下面是他的一些履历: diff --git a/content/post/news/2024-01-14-third-meetup.smd b/content/post/news/2024-01-14-third-meetup.smd index e7024d6..052db65 100644 --- a/content/post/news/2024-01-14-third-meetup.smd +++ b/content/post/news/2024-01-14-third-meetup.smd @@ -18,7 +18,7 @@ - 公众号运营 - 如何与其他社区互动 -# [公众号运营]($section.id('公众号运营')) +# [公众号运营]($heading.id('public-account-operation')) 这是最近群里聊到的问题,由于 Zig 语言本身属于较新的技术,因此社区内资料比较少,这导致很多感兴趣的人没有一个好的学习途径。 @@ -40,12 +40,12 @@ 主要参与人员:西瓜、金中甲 -# [社区互动]($section.id('社区互动')) +# [社区互动]($heading.id('community-interaction')) 目前我们的成员在 Zig 的实践方面相对较少,因此决定目前不过多的去宣传,在积攒了一些实际项目经验后,再来考虑。 -# [欢迎更多朋友加入 ZigCC]($section.id('welcome-more-friends-to-join-zigcc')) +# [欢迎更多朋友加入 ZigCC]($heading.id('welcome-more-friends-to-join-zigcc')) 现在回看,距离第一次 ZigCC 线上会议过了一个月,经过 ZigCC 成员的努力,还是交出了一份比较满意的答卷,[cookbook](https://github.com/zigcc/zig-cookbook) diff --git a/format.ts b/format.ts new file mode 100644 index 0000000..6101365 --- /dev/null +++ b/format.ts @@ -0,0 +1,153 @@ +import * as fs from 'fs/promises'; +import * as path from 'path'; +import * as tencentcloud from 'tencentcloud-sdk-nodejs'; + +// 腾讯云 TMT 客户端 +const TmtClient = tencentcloud.tmt.v20180321.Client; + +// 配置腾讯云凭证 +const clientConfig = { + credential: { + secretId: process.env.TENCENT_SECRET_ID || 'YOUR_SECRET_ID', // 替换为您的 SecretId + secretKey: process.env.TENCENT_SECRET_KEY || 'YOUR_SECRET_KEY', // 替换为您的 SecretKey + }, + region: 'ap-guangzhou', // 选择合适的区域,例如广州 + profile: { + httpProfile: { + endpoint: 'tmt.tencentcloudapi.com', + }, + }, +}; + +// 初始化 TMT 客户端 +const client = new TmtClient(clientConfig); + +// 翻译接口:使用腾讯云 TMT 批量翻译 API +async function translateToEnglish(texts: string[]): Promise { + try { + const params = { + SourceTextList: texts, + Source: 'zh', + Target: 'en', + ProjectId: 0, // 替换为您的项目 ID(通常为 0 即可) + }; + + const response = await client.TextTranslateBatch(params); + return response.TargetTextList || texts; // 翻译失败时返回原文本 + } catch (error) { + console.error(`Batch translation error for texts:`, error); + return texts; // 翻译失败时返回原文本 + } +} + +// 检查是否为中文 +function isChinese(text: string): boolean { + return /[\u4e00-\u9fa5]/.test(text); +} + +// 检查 ID 是否符合规范(只包含小写字母、数字和连字符) +function isValidIdFormat(id: string): boolean { + return /^[a-z0-9-]+$/.test(id); +} + +// 格式化 ID:转换为小写,用连字符替换空格,移除特殊字符 +function formatId(id: string): string { + return id + .toLowerCase() + .replace(/\s+/g, '-') + .replace(/[^a-z0-9-]/g, ''); +} + +// 处理单个文件 +async function processFile(filePath: string): Promise { + try { + let content = await fs.readFile(filePath, 'utf-8'); + const headingIdRegex = /heading\.id\(['"](.*?)['"]\)/g; + const matches = [...content.matchAll(headingIdRegex)]; + const chineseIds: string[] = []; + const idMap: { [original: string]: string } = {}; + let modified = false; + + // 收集需要翻译的中文 ID 和需要格式化的英文 ID + for (const match of matches) { + const id = match[1]; + if (isChinese(id) || !isValidIdFormat(id)) { + if (isChinese(id)) { + chineseIds.push(id); + } + idMap[id] = match[0]; // 存储原始匹配字符串 + } + } + + if (Object.keys(idMap).length > 0) { + // 批量翻译中文 ID + const translatedIds = chineseIds.length > 0 ? await translateToEnglish(chineseIds) : []; + + // 替换所有不符合规范的 ID + for (const originalId of Object.keys(idMap)) { + let formattedId: string; + if (isChinese(originalId)) { + // 中文 ID:使用翻译结果 + const index = chineseIds.indexOf(originalId); + const translated = translatedIds[index] || originalId; // 回退到原文本 + formattedId = formatId(translated); + } else { + // 英文 ID:直接格式化 + formattedId = formatId(originalId); + } + content = content.replace(idMap[originalId], `heading.id('${formattedId}')`); + modified = true; + } + + if (modified) { + await fs.writeFile(filePath, content, 'utf-8'); + console.log(`Processed: ${filePath}`); + } + } + } catch (error) { + console.error(`Error processing ${filePath}:`, error); + } +} + +// 处理目录 +async function processDirectory(dirPath: string): Promise { + try { + const entries = await fs.readdir(dirPath, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dirPath, entry.name); + if (entry.isDirectory()) { + await processDirectory(fullPath); + } else if (entry.isFile() && (entry.name.endsWith('.smd'))) { + await processFile(fullPath); + } + } + } catch (error) { + console.error(`Error processing directory ${dirPath}:`, error); + } +} + +// 主函数 +async function main(): Promise { + const targetPath = process.argv[2]; + + if (!targetPath) { + console.error('Please provide a file or directory path'); + process.exit(1); + } + + try { + const stats = await fs.stat(targetPath); + if (stats.isDirectory()) { + await processDirectory(targetPath); + } else { + await processFile(targetPath); + } + console.log('Processing completed'); + } catch (error) { + console.error('Error:', error); + process.exit(1); + } +} + +main(); \ No newline at end of file