# 引导 Zig Fmt 在过去的几个月里,我一直在研究 Zig 格式化工具(`zig fmt`)的演变方向,最终形成了[这份提案](https://github.com/ziglang/zig/issues/20078)。由于这是一个颇具争议的话题,我想在这篇文章中详细阐述其中的权衡考量。 ## 现状 `zig fmt` 是一个固执己见的格式化工具:它会将 Zig 代码格式化为单一的规范形式,且不受用户配置的影响。这类工具(如 `gofmt`、`prettier`)有一个显著优势:当整个生态系统都使用同一格式化工具时,就能消除围绕风格的无谓争论,并确保所有代码的外观一致。 然而,现有的 `zig fmt` 存在一个问题:它实际上并不是完全固执己见的——格式化结果会根据用户的输入而变化。 以下面这个例子为例: ```zig const x = foo(1, 2, 3); ``` 如果你在最后一个参数后面加上一个逗号: ```zig const x = foo(1, 2, 3,); ``` `zig fmt` 会将其格式化为: ```zig const x = foo( 1, 2, 3, ); ``` 这意味着代码的格式化方式(单行还是多行)取决于用户是否添加了尾随逗号。此外,`zig fmt` 对于其他一些构造也并非完全固执己见,例如注释的位置。 ## 问题所在 为什么这是个问题呢?毕竟,根据尾随逗号来决定格式化方式,这是一种广为人知且合理的约定。 问题在于,这种方案实际上给了用户两种选择:单行格式和多行格式。这意味着用户必须做出决定,而这恰恰是格式化工具本应消除的那类决策。 更糟糕的是,这个决定并没有一个客观正确的答案,因为适合单行还是多行,往往取决于行的长度——而行的长度会随着变量名、参数等的变化而改变。 举个例子,假设你有: ```zig const x = foo(1, 2, 3); ``` 这段代码很短,放在一行完全没问题。但如果函数名变长了呢? ```zig const x = a_longer_function_name(1, 2, 3); ``` 还是挺短的。那如果更长呢? ```zig const x = a_much_much_longer_function_name(argument_one, argument_two, argument_three); ``` 这行已经相当长了,或许应该换成多行格式: ```zig const x = a_much_much_longer_function_name( argument_one, argument_two, argument_three, ); ``` 但在当前的 `zig fmt` 机制下,你需要手动添加尾随逗号来触发这个格式化。如果你重构代码,将函数名改短,或者把参数替换为更短的名称,那么多行格式可能就不再必要了——但 `zig fmt` 不会自动帮你切换回单行格式,因为它会把尾随逗号视为"保持多行"的明确指令。 ## 解决方案 解决方案是让 `zig fmt` 基于行长度自动决定使用单行还是多行格式。具体来说:如果一个表达式能放在一行内(不超过某个长度限制,比如 100 个字符),就使用单行格式;否则,使用多行格式。 这正是 `prettier` 的工作方式,也是大多数现代格式化工具所采用的方案。 这意味着尾随逗号将不再具有语义上的格式化含义。你可以写: ```zig const x = foo(1, 2, 3,); ``` `zig fmt` 会根据行长度自动决定使用哪种格式,而不是盲目地遵循尾随逗号的指示。 ## 争议点 这项改动之所以有争议,主要有以下几个原因: **1. 人们习惯了现有的行为** 很多 Zig 开发者已经习惯于用尾随逗号来控制格式化。改变这一行为会打破他们的工作流程。 **2. 基于行长度的格式化可能产生令人惊讶的结果** 当你重命名一个变量,导致某行超过了长度限制,整个表达式的格式可能会突然从单行变成多行。这种"蝴蝶效应"可能让人感到困惑。 **3. 需要确定合适的行长度限制** 100 个字符?80 个字符?这本身也是一个需要决策的问题,尽管它只是一次性的决策,而不是每次写代码都要面对的决策。 ## 结论 尽管存在争议,我认为基于行长度的自动格式化是正确的方向。它让 `zig fmt` 真正成为一个固执己见的格式化工具,消除了用户需要做出的格式化决策,并确保代码在重构后始终保持最优的格式。 这与 Zig 语言的整体设计哲学是一致的:减少不必要的复杂性,让工具为开发者做出明智的决策,从而让开发者能够专注于真正重要的事情。

Lobsters Hottest 工具

摘要

# 两个让 `zig fmt` 更好用的技巧 Zig 配备了一个内置的代码格式化工具 `zig fmt`。与其他语言的格式化工具不同,`zig fmt` 是"可操控的"——某些语法结构会影响格式化的输出结果。本文将介绍两个实用技巧。 ## 技巧一:尾随逗号控制布局 `zig fmt` 会根据是否存在尾随逗号来决定参数的排列方式。 **没有尾随逗号**时,格式化工具会尝试将所有参数放在同一行: ```zig const result = myFunction(argument1, argument2, argument3); ``` **有尾随逗号**时,格式化工具会将每个参数单独放在一行: ```zig const result = myFunction( argument1, argument2, argument3, ); ``` 这个规则同样适用于函数定义的参数列表、结构体字段、枚举变体等场景。 ```zig // 单行:无尾随逗号 const Point = struct { x: f32, y: f32 }; // 多行:有尾随逗号 const Point = struct { x: f32, y: f32, }; ``` 这意味着你可以通过添加或删除尾随逗号来主动控制格式化的输出,而不必与格式化工具"博弈"。想要多行展示?加上尾随逗号。想要单行展示?去掉它。 同样的逻辑也适用于换行符。如果你在参数之间手动添加了换行符,`zig fmt` 会尊重这个选择并保留多行格式——前提是同时带有尾随逗号。 ## 技巧二:数组的列式格式化 对于数值数组,`zig fmt` 支持一种特殊的列式格式化方式,非常适合用来表示矩阵或表格数据。 只需在数组元素之间手动插入换行符,`zig fmt` 就会将数据对齐成整洁的列式布局: ```zig // 格式化前(你写的) const matrix = [_]f32{ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, }; ``` ```zig // 格式化后(zig fmt 输出) const matrix = [_]f32{ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, }; ``` `zig fmt` 会识别出你在每行放置了相同数量的元素,并将各列对齐,使代码更具可读性。这对于表示变换矩阵、查找表或任何具有内在行列结构的数据来说极为方便。 ```zig // 一个更直观的例子:查找表 const sine_table = [_]f32{ 0.000, 0.174, 0.342, 0.500, 0.643, 0.766, 0.866, 0.940, 0.985, 1.000, 0.985, 0.940, 0.866, 0.766, 0.643, 0.500, }; ``` ## 小结 `zig fmt` 的"可操控"设计哲学让格式化工具成为你的合作伙伴,而不是独裁者: - **尾随逗号** → 强制多行展开 - **无尾随逗号** → 允许单行折叠 - **手动换行 + 统一列数** → 触发列式对齐 掌握这两个技巧,你就能在享受自动格式化便利的同时,保留对代码视觉呈现的精确控制。

<p>我很喜欢这种考虑换行符和每行字符数的设计理念!大家在其他语言格式化工具中还注意到并喜欢哪些类似的特性?</p> <p><a href="https://lobste.rs/s/auxtwd/steering_zig_fmt">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/05/09 06:36

# 驾驭 Zig Fmt 来源:https://matklad.github.io/2026/05/08/steering-zig-fmt.html 2026 年 5 月 8 日 — 关于高效使用 `zig fmt` 的两个技巧。如果你在写 Zig,或者正在实现一个代码格式化工具,不妨读一读。 对我来说,`zig fmt` 比我用过的所有其他格式化工具都要好:`rustfmt`、IntelliJ 内置的格式化工具、`deno fmt`。`zig fmt` 是可驾驭的。对于每一种语法结构,它都提供了几种不同的排版方式,具体使用哪种,取决于文件中当前的写法。 还是用两个例子来说明更直观: ``` f(1, 2, 3); // -> zig fmt -> f(1, 2, 3); ``` ``` f(1, 2, 3,); // -> zig fmt -> f( 1, 2, 3, ); ``` 根据是否有尾随逗号,函数调用会被格式化成单行,或者每个参数独占一行。 在实际使用中,这意味着你可以*自己决定*代码的排版方式:加几个 `,`,按下重新格式化快捷键(我用的是 `, p`(https://matklad.github.io/2024/10/08/two-tips.html#s-1)),`zig fmt` 就会帮你完成剩余的工作。在我看来,这比让格式化工具自行猜测要好得多。优秀格式化的 90% 在于逻辑块之间的空行,以及对中间变量的合理运用——所以与其把这些关键选择权完全交出去,不如主动把控它们。 我知道一个非平凡的格式化定制点:数组的列对齐布局: ``` .{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, }; ``` 你可能会以为尾随逗号会导致每行一个数字的布局,但对于数组,`zig fmt` 还会注意第一个换行的位置。在这个例子中,换行出现在前三个元素之后,因此每行排列三个数字,并对齐: ``` .{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, }; ``` 这也太酷了吧! 此外,善用 `++`,你还可以灵活调整每行的元素数量。当我需要向子进程传递 `--key` `value` 键值对时,我常常会采用这样的格式: ``` try run(&(.{ "aws", "s3", "sync", path, url } ++ .{ "--include", "*.html", "--include", "*.xml", "--metadata-directive", "REPLACE", "--cache-control", "max-age=0", })); ```

相似文章

Zig 示例教程

Hacker News Top

通过带注释的示例,对 Zig 编程语言进行实践性介绍,涵盖从基础到高级的主题。灵感来源于 Go by Example。

Zig 的数组结构体 (2024)

Hacker News Top

说明 Zig 的 comptime 和类型反射如何支持创建像 MultiArrayList 这样的数组结构体 (SoA) 数据结构,从而提升高性能应用中的缓存性能。

用 Zig 写一个 C 编译器

Hacker News Top

一位开发者记录了用 Zig 语言、按照 Nora Sandler 的教程系列构建名为 paella 的 C 编译器的全过程。

以Zig风格构建你的项目

Lobsters Hottest

作者详细介绍了构建一个名为bygge-zig的工具,该工具使用Zig构建系统来编译Rust项目,用更少的代码行复制了Cargo的功能,并突出了其中的差异和挑战。