概览
当团队的命名、注释与模块布局遵循可预测的节奏时,协作更敏捷。本附录提炼“家规”风格为速查,便于在评审 PR 或搭建新模块时随手翻阅。
Zig 0.15.2 收紧格式化输出、稳定文档注释处理并明确测试体验;采用这些默认值意味着更少的约定协商时间与更多的行为验证时间。v0.15.2
学习目标
- 通过扫描文档注释、类型、函数和测试的规范排序,快速审查模块。
- 阐述 Zig 中"紧凑错误词汇"的含义,以及何时应优先选择定制错误集而非
anyerror。 - 将确定性测试与所记录的代码并排放置,同时不牺牲大文件的可读性。
参考文献:见各章节末尾
语气与命名速览
可读代码始于叙述与标识符的契合:文档注释应使用与导出符号实现相同的名词,而辅助函数则保持动词简短有力。fmt.zig 遵循此模式使评审者能专注于语义而非争论措辞选择。
命名、注释与 Writer
该示例将模块级叙述与聚焦的文档注释配合使用,并采用固定缓冲 writer,使测试不触及分配器。Io.zig
Zig
// ! 演示小型诊断辅助函数的命名和文档约定。
const std = @import("std");
// / 表示诊断期间捕获的带标签的温度读数。
pub const TemperatureReading = struct {
label: []const u8,
value_celsius: f32,
// / 使用规范的大小写和单位将读数写入提供的写入器。
pub fn format(self: TemperatureReading, writer: anytype) !void {
try writer.print("{s}: {d:.1}°C", .{ self.label, self.value_celsius });
}
};
// / 使用给定标签和摄氏温度值创建读数。
pub fn createReading(label: []const u8, value_celsius: f32) TemperatureReading {
return .{
.label = label,
.value_celsius = value_celsius,
};
}
test "temperature readings print with consistent label casing" {
const reading = createReading("CPU", 72.25);
var backing: [64]u8 = undefined;
var stream = std.io.fixedBufferStream(&backing);
try reading.format(stream.writer());
const rendered = stream.getWritten();
try std.testing.expectEqualStrings("CPU: 72.3°C", rendered);
}
运行
Shell
$ zig test 01_naming_and_comments.zig输出
Shell
All 1 tests passed.在代码前格式化描述性句子鼓励读者一并浏览类型签名与测试;使术语与文档注释保持一致体现了第 36 章的建议。36
紧凑的错误词汇
精确的错误集在兼顾调用者体验与轻量级控制流间取得平衡;我们不返回 anyerror,而是精确列出解析器可达的状态,并将其提升为公共 API 表面。math.zig
Zig
// ! 保持数值解析器的错误词汇紧凑,以便调用者能精确响应。
const std = @import("std");
// / 枚举解析器可以向其调用者报告的故障模式。
pub const ParseCountError = error{
EmptyInput,
InvalidDigit,
Overflow,
};
// / 解析十进制计数器,同时保留描述性错误信息。
pub fn parseCount(input: []const u8) ParseCountError!u32 {
if (input.len == 0) return ParseCountError.EmptyInput;
var acc: u64 = 0;
for (input) |char| {
if (char < '0' or char > '9') return ParseCountError.InvalidDigit;
const digit: u64 = @intCast(char - '0');
acc = acc * 10 + digit;
if (acc > std.math.maxInt(u32)) return ParseCountError.Overflow;
}
return @intCast(acc);
}
test "parseCount reports invalid digits precisely" {
try std.testing.expectEqual(@as(u32, 42), try parseCount("42"));
try std.testing.expectError(ParseCountError.InvalidDigit, parseCount("4a"));
try std.testing.expectError(ParseCountError.EmptyInput, parseCount(""));
try std.testing.expectError(ParseCountError.Overflow, parseCount("42949672960"));
}
运行
Shell
$ zig test 02_error_vocabulary.zig输出
Shell
All 1 tests passed.测试套件证明各分支均可到达,防止“死字符串”,并教会使用者无需阅读实现即可知道应在何处对名称进行switch。36
模块布局清单
当文件导出配置助手时,请将公共接口置于前部、将私有校验器收纳其后,并以表驱动测试收尾,使其可读性如同文档。12
Zig
// ! 突出显示具有集中辅助函数和测试的分层模块布局。
const std = @import("std");
// / 规范化用户提供的重试策略时可能出现的错误。
pub const RetryPolicyError = error{
ZeroAttempts,
ExcessiveDelay,
};
// / 封装网络客户端的重试行为,包括合理的默认值。
pub const RetryPolicy = struct {
max_attempts: u8 = 3,
delay_ms: u32 = 100,
/// 指示指数退避是否激活。
pub fn isBackoffEnabled(self: RetryPolicy) bool {
return self.delay_ms > 0 and self.max_attempts > 1;
}
};
// 由配置文件或 CLI 标志提供的部分选项。
pub const PartialRetryOptions = struct {
max_attempts: ?u8 = null,
delay_ms: ?u32 = null,
};
// 从可选覆盖构建重试策略,同时保持默认推理集中。
pub fn makeRetryPolicy(options: PartialRetryOptions) RetryPolicy {
return RetryPolicy{
.max_attempts = options.max_attempts orelse 3,
.delay_ms = options.delay_ms orelse 100,
};
}
fn validate(policy: RetryPolicy) RetryPolicyError!RetryPolicy {
if (policy.max_attempts == 0) return RetryPolicyError.ZeroAttempts;
if (policy.delay_ms > 60_000) return RetryPolicyError.ExcessiveDelay;
return policy;
}
// 生成一个经过验证的策略,强调从原始输入到受限输出的流程。
pub fn finalizeRetryPolicy(options: PartialRetryOptions) RetryPolicyError!RetryPolicy {
const policy = makeRetryPolicy(options);
return validate(policy);
}
test "finalize rejects zero attempts" {
try std.testing.expectError(
RetryPolicyError.ZeroAttempts,
finalizeRetryPolicy(.{ .max_attempts = 0 }),
);
}
test "finalize accepts defaults" {
const policy = try finalizeRetryPolicy(.{});
try std.testing.expectEqual(@as(u8, 3), policy.max_attempts);
try std.testing.expect(policy.isBackoffEnabled());
}
运行
Shell
$ zig test 03_module_layout.zig输出
Shell
All 2 tests passed.将错误集置于顶部可保持类型图的清晰,并映射 std.testing 在依赖代码旁物化不变量的方式。testing.zig
随手可用的模式
- 为模块级叙述保留
//!,为 API 文档保留///,使生成的引用在各包间保持一致的语调。36 - 为每个公开的辅助函数配对专注的测试块;Zig 的测试运行器使并置测试零成本,它们同时也是可执行的使用示例。
- 当格式化器重排签名时,请接受其裁决——编辑器与 CI 间的一致性是 0.15.x 的重要体验改进之一。
注意与警示
- 不要抑制来自
zig fmt的警告;而应调整代码使默认设置成功,并在贡献指南中记录任何不可避免的偏离。36 - 保持项目本地的 lint 脚本与上游 Zig 版本同步,以便在工具链升级期间将琐事开销降至最低。
- 若你的 API 发出来自
std的容器类型,请在文档注释中引用其确切字段名——调用者可直接跳转到zig/lib/std确认语义。hash_map.zig
练习
- 重写你最近的某个模块,按上面显示的顺序将常量、类型、函数和测试分组,然后运行
zig fmt确认结构保持稳定。36 - 扩展
parseCount以接受下划线提高可读性,同时保持严格的错误词汇;为新分支添加针对性测试。 - 使用
zig build doc为项目生成 HTML 文档,并审查//!和///注释如何呈现——调整叙述直到输出流畅可读。