概述
当团队的命名、注释和模块布局遵循可预测的节奏时,团队保持敏捷。本附录将内部风格提炼为快速参考,您可以将其保留为打开状态,同时审查拉取请求或脚手架新模块。
Zig 0.15.2收紧了格式化器输出,稳定了文档注释处理,并澄清了测试人体工程学;采用这些默认值意味着在谈判惯例上花费更少的时间,在验证行为上花费更多的时间。v0.15.2
学习目标
- 通过扫描文档注释、类型、函数和测试的规范排序来快速审计模块。
- 描述Zig中"严格错误词汇"的含义,以及何时更喜欢自定义错误集而不是
anyerror。 - 将确定性测试与其记录的代码连接起来,而不牺牲大文件中的可读性。
_Refs: _
一目了然的语调和命名
可读性代码从散文和标识符之间的对齐开始:文档注释应该与导出符号实现相同的名词,而辅助函数保持动词简短和主动。fmt.zig 遵循此模式让审查者专注于语义,而不是辩论词汇选择。
命名、注释和写入器
此示例将模块级叙述与聚焦的文档注释配对,并使用固定缓冲区写入器,以便测试从不接触分配器。Io.zig
Zig
//! Demonstrates naming and documentation conventions for a small diagnostic helper.
const std = @import("std");
/// Represents a labelled temperature reading captured during diagnostics.
pub const TemperatureReading = struct {
label: []const u8,
value_celsius: f32,
/// Writes the reading to the provided writer using canonical casing and units.
pub fn format(self: TemperatureReading, writer: anytype) !void {
try writer.print("{s}: {d:.1}°C", .{ self.label, self.value_celsius });
}
};
/// Creates a reading with the given label and temperature value in 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
//! Keeps error vocabulary tight for a numeric parser so callers can react precisely.
const std = @import("std");
/// Enumerates the failure modes that the parser can surface to its callers.
pub const ParseCountError = error{
EmptyInput,
InvalidDigit,
Overflow,
};
/// Parses a decimal counter while preserving descriptive error information.
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
//! Highlights a layered module layout with focused helper functions and tests.
const std = @import("std");
/// Errors that can emerge while normalizing user-provided retry policies.
pub const RetryPolicyError = error{
ZeroAttempts,
ExcessiveDelay,
};
/// Encapsulates retry behaviour for a network client, including sensible defaults.
pub const RetryPolicy = struct {
max_attempts: u8 = 3,
delay_ms: u32 = 100,
/// Indicates whether exponential backoff is active.
pub fn isBackoffEnabled(self: RetryPolicy) bool {
return self.delay_ms > 0 and self.max_attempts > 1;
}
};
/// Partial options provided by configuration files or CLI flags.
pub const PartialRetryOptions = struct {
max_attempts: ?u8 = null,
delay_ms: ?u32 = null,
};
/// Builds a retry policy from optional overrides while keeping default reasoning centralized.
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;
}
/// Produces a validated policy, emphasising the flow from raw input to constrained output.
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的测试运行器使并列测试免费,并且它们兼作可执行的使用示例。
- 当格式化器重新流式传输签名时,接受其判断——编辑器之间的一致性是0.15.x中生活质量的主要改进之一。
注意事项和警告
- 不要抑制来自
zig fmt的警告;相反,调整代码使默认值成功,并在您的贡献指南中记录任何不可避免的差异。36 - 使项目本地lint脚本与上游Zig版本保持同步,以便在工具链升级期间保持繁琐工作的低变动。
- 如果您的API从
std发出容器类型,请在文档注释中引用其确切的字段名——调用者可以直接跳转到zig/lib/std来确认语义。hash_map.zig
练习
- 通过按上面显示的顺序对常量、类型、函数和测试进行分组来重写您最近的模块之一,然后运行
zig fmt来确认结构保持稳定。36 - 扩展
parseCount以接受下划线以提高可读性,同时保持严格的错误词汇;为新分支添加针对性测试。 - 使用
zig build doc为项目生成HTML文档,并查看//!和///注释如何出现——调整散文直到输出阅读流畅。