Chapter 55Style Guide Highlights

附录A. 风格指南要点

概述

当团队的命名、注释和模块布局遵循可预测的节奏时,团队保持敏捷。本附录将内部风格提炼为快速参考,您可以将其保留为打开状态,同时审查拉取请求或脚手架新模块。

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文档,并查看//!///注释如何出现——调整散文直到输出阅读流畅。

替代方案和边界情况

  • 一些团队更喜欢完全分离的测试文件;如果您这样做,请采用相同的命名和文档注释模式,以便搜索结果保持可预测。36
  • 对于公开comptime重API的模块,包含一个test "comptime"块,以便这些准则仍然提供可运行覆盖。15
  • 在供应商第三方代码时,在简短的README中标注与此风格的偏差,以便审查者知道差异是有意的。Build.zig

Help make this chapter better.

Found a typo, rough edge, or missing explanation? Open an issue or propose a small improvement on GitHub.