Skip to content

Zig 风格指南

良好的代码风格是团队协作和代码维护的基础。本章将介绍 Zig 官方推荐的代码风格和最佳实践。

命名约定

变量和函数命名

使用 snake_case 命名变量和函数:

zig
const std = @import("std");

// ✅ 好的命名
const max_buffer_size = 4096;
var current_user_count: u32 = 0;
const default_timeout_ms = 5000;

fn calculateDistance(point1: Point, point2: Point) f64 {
    const dx = point1.x - point2.x;
    const dy = point1.y - point2.y;
    return @sqrt(dx * dx + dy * dy);
}

fn processUserInput(input_buffer: []const u8) ![]u8 {
    // 处理用户输入
    return input_buffer;
}

// ❌ 避免的命名
// const maxBufferSize = 4096;  // camelCase
// const MaxBufferSize = 4096;  // PascalCase
// const MAX_BUFFER_SIZE = 4096; // 常量应该用小写

类型命名

使用 PascalCase 命名类型:

zig
const std = @import("std");

// ✅ 好的类型命名
const Point = struct {
    x: f64,
    y: f64,
};

const Color = enum {
    Red,
    Green,
    Blue,
    Yellow,
};

const HttpRequest = struct {
    method: HttpMethod,
    url: []const u8,
    headers: std.StringHashMap([]const u8),
};

const DatabaseConnection = struct {
    host: []const u8,
    port: u16,
    username: []const u8,
    
    pub fn connect(self: *DatabaseConnection) !void {
        // 连接逻辑
    }
};

// ❌ 避免的命名
// const httpRequest = struct { ... };  // camelCase
// const HTTP_REQUEST = struct { ... }; // SCREAMING_SNAKE_CASE

常量命名

使用 snake_case 命名常量,全大写仅用于真正的编译时常量:

zig
const std = @import("std");

// ✅ 好的常量命名
const default_port = 8080;
const max_connections = 1000;
const api_version = "v1.0";

// 编译时已知的数学常量可以使用大写
const PI = 3.14159265358979323846;
const E = 2.71828182845904523536;

// 配置常量
const config = struct {
    const server_timeout = 30; // 秒
    const retry_attempts = 3;
    const buffer_size = 4096;
};

// ❌ 避免的命名
// const DEFAULT_PORT = 8080;     // 不必要的大写
// const MaxConnections = 1000;   // PascalCase
// const apiVersion = "v1.0";     // camelCase

枚举值命名

使用 PascalCase 命名枚举值:

zig
const std = @import("std");

// ✅ 好的枚举命名
const HttpMethod = enum {
    Get,
    Post,
    Put,
    Delete,
    Patch,
    Head,
    Options,
};

const LogLevel = enum {
    Debug,
    Info,
    Warning,
    Error,
    Critical,
};

const ConnectionState = enum {
    Disconnected,
    Connecting,
    Connected,
    Reconnecting,
    Failed,
};

// ❌ 避免的命名
// const HttpMethod = enum {
//     GET,    // 全大写
//     get,    // 全小写
//     Post,   // 不一致
// };

代码格式化

缩进和空格

使用 4 个空格进行缩进,不使用制表符:

zig
const std = @import("std");

// ✅ 正确的缩进
pub fn main() void {
    const numbers = [_]i32{ 1, 2, 3, 4, 5 };
    
    for (numbers) |number| {
        if (number % 2 == 0) {
            std.debug.print("偶数: {}\n", .{number});
        } else {
            std.debug.print("奇数: {}\n", .{number});
        }
    }
    
    const result = switch (numbers.len) {
        0 => "空数组",
        1 => "单个元素",
        2...5 => "少量元素",
        else => "大量元素",
    };
    
    std.debug.print("数组描述: {s}\n", .{result});
}

行长度

建议每行不超过 100 个字符,必要时可以适当延长:

zig
const std = @import("std");

// ✅ 合理的行长度
pub fn processLongParameterList(
    first_parameter: []const u8,
    second_parameter: i32,
    third_parameter: bool,
    fourth_parameter: ?*SomeStruct,
) !ProcessResult {
    // 函数体
}

// ✅ 长字符串的处理
const long_message = 
    "这是一个很长的消息,需要分成多行来保持代码的可读性。" ++
    "我们可以使用字符串连接来处理这种情况。";

// ✅ 长表达式的换行
const complex_calculation = (first_value * second_value) +
    (third_value / fourth_value) -
    (fifth_value % sixth_value);

空行使用

合理使用空行来分隔逻辑块:

zig
const std = @import("std");

// 导入后空一行
const ArrayList = std.ArrayList;
const Allocator = std.mem.Allocator;

// 常量定义
const DEFAULT_CAPACITY = 16;
const MAX_RETRIES = 3;

// 类型定义前后空行
const User = struct {
    id: u32,
    name: []const u8,
    email: []const u8,
    
    // 方法间空行
    pub fn init(id: u32, name: []const u8, email: []const u8) User {
        return User{
            .id = id,
            .name = name,
            .email = email,
        };
    }
    
    pub fn isValid(self: User) bool {
        return self.name.len > 0 and self.email.len > 0;
    }
};

// 函数间空行
pub fn main() void {
    const user = User.init(1, "张三", "zhangsan@example.com");
    
    if (user.isValid()) {
        std.debug.print("用户有效: {s}\n", .{user.name});
    }
}

注释风格

文档注释

使用 /// 进行文档注释:

zig
const std = @import("std");

/// 表示二维空间中的一个点
const Point = struct {
    /// X 坐标
    x: f64,
    /// Y 坐标
    y: f64,
    
    /// 创建一个新的点
    /// 
    /// 参数:
    /// - x: X 坐标值
    /// - y: Y 坐标值
    /// 
    /// 返回: 新创建的 Point 实例
    pub fn init(x: f64, y: f64) Point {
        return Point{ .x = x, .y = y };
    }
    
    /// 计算到另一个点的距离
    /// 
    /// 参数:
    /// - other: 目标点
    /// 
    /// 返回: 两点间的欧几里得距离
    pub fn distanceTo(self: Point, other: Point) f64 {
        const dx = self.x - other.x;
        const dy = self.y - other.y;
        return @sqrt(dx * dx + dy * dy);
    }
    
    /// 检查点是否在原点
    /// 
    /// 返回: 如果点在原点则返回 true
    pub fn isOrigin(self: Point) bool {
        return self.x == 0.0 and self.y == 0.0;
    }
};

普通注释

使用 // 进行普通注释:

zig
const std = @import("std");

pub fn processData(data: []const u8) ![]u8 {
    // 验证输入数据
    if (data.len == 0) {
        return error.EmptyInput;
    }
    
    // 分配输出缓冲区
    var allocator = std.heap.page_allocator;
    var result = try allocator.alloc(u8, data.len * 2);
    
    // 处理每个字节
    for (data, 0..) |byte, i| {
        // 将每个字节转换为十六进制表示
        const hex_chars = "0123456789ABCDEF";
        result[i * 2] = hex_chars[byte >> 4];
        result[i * 2 + 1] = hex_chars[byte & 0x0F];
    }
    
    return result;
}

顶级文档注释

使用 //! 进行文件级别的文档注释:

zig
//! 数学工具库
//! 
//! 这个模块提供了各种数学计算功能,包括:
//! - 基本几何计算
//! - 统计函数
//! - 数值分析工具
//! 
//! 示例用法:
//! ```zig
//! const math_utils = @import("math_utils.zig");
//! const point = math_utils.Point.init(3.0, 4.0);
//! const distance = point.distanceTo(math_utils.Point.init(0.0, 0.0));
//! ```

const std = @import("std");

// 模块内容...

错误处理风格

错误传播

优先使用 try 进行错误传播:

zig
const std = @import("std");

const FileError = error{
    NotFound,
    PermissionDenied,
    ReadError,
};

// ✅ 好的错误处理
pub fn readConfigFile(allocator: std.mem.Allocator, path: []const u8) ![]u8 {
    const file = try std.fs.cwd().openFile(path, .{});
    defer file.close();
    
    const file_size = try file.getEndPos();
    const contents = try allocator.alloc(u8, file_size);
    _ = try file.readAll(contents);
    
    return contents;
}

// ✅ 使用 catch 提供默认值
pub fn getConfigValue(config: []const u8, key: []const u8) []const u8 {
    return parseConfigValue(config, key) catch "default_value";
}

// ✅ 使用 if-else 进行错误处理
pub fn processFile(path: []const u8) void {
    if (readConfigFile(std.heap.page_allocator, path)) |contents| {
        defer std.heap.page_allocator.free(contents);
        std.debug.print("文件内容: {s}\n", .{contents});
    } else |err| {
        std.debug.print("读取文件失败: {}\n", .{err});
    }
}

fn parseConfigValue(config: []const u8, key: []const u8) ![]const u8 {
    // 解析逻辑
    _ = config;
    _ = key;
    return "parsed_value";
}

错误集合定义

将相关的错误组织到有意义的错误集合中:

zig
const std = @import("std");

// ✅ 按功能组织错误
const NetworkError = error{
    ConnectionFailed,
    Timeout,
    InvalidAddress,
    ProtocolError,
};

const DatabaseError = error{
    ConnectionFailed,
    QueryFailed,
    TransactionFailed,
    ConstraintViolation,
};

const ValidationError = error{
    InvalidInput,
    MissingField,
    FormatError,
    RangeError,
};

// ✅ 组合错误集合
const ApplicationError = NetworkError || DatabaseError || ValidationError;

pub fn processRequest(request: []const u8) ApplicationError![]const u8 {
    // 验证请求
    try validateRequest(request);
    
    // 查询数据库
    const data = try queryDatabase(request);
    
    // 发送网络请求
    const response = try sendNetworkRequest(data);
    
    return response;
}

fn validateRequest(request: []const u8) ValidationError!void {
    if (request.len == 0) {
        return ValidationError.InvalidInput;
    }
}

fn queryDatabase(request: []const u8) DatabaseError![]const u8 {
    _ = request;
    return "database_result";
}

fn sendNetworkRequest(data: []const u8) NetworkError![]const u8 {
    _ = data;
    return "network_response";
}

结构体和枚举风格

结构体定义

zig
const std = @import("std");

// ✅ 好的结构体风格
const HttpServer = struct {
    // 字段按重要性和大小排序
    allocator: std.mem.Allocator,
    address: std.net.Address,
    max_connections: u32,
    timeout_ms: u32,
    is_running: bool,
    
    const Self = @This();
    
    // 构造函数
    pub fn init(allocator: std.mem.Allocator, address: std.net.Address) Self {
        return Self{
            .allocator = allocator,
            .address = address,
            .max_connections = 100,
            .timeout_ms = 5000,
            .is_running = false,
        };
    }
    
    // 公共方法
    pub fn start(self: *Self) !void {
        if (self.is_running) {
            return error.AlreadyRunning;
        }
        
        // 启动逻辑
        self.is_running = true;
        std.debug.print("服务器已启动\n", .{});
    }
    
    pub fn stop(self: *Self) void {
        if (!self.is_running) {
            return;
        }
        
        // 停止逻辑
        self.is_running = false;
        std.debug.print("服务器已停止\n", .{});
    }
    
    // 私有方法
    fn handleConnection(self: *Self, connection: std.net.Stream) void {
        _ = self;
        _ = connection;
        // 处理连接
    }
};

枚举定义

zig
const std = @import("std");

// ✅ 好的枚举风格
const TaskStatus = enum {
    Pending,
    Running,
    Completed,
    Failed,
    Cancelled,
    
    // 枚举方法
    pub fn isFinished(self: TaskStatus) bool {
        return switch (self) {
            .Completed, .Failed, .Cancelled => true,
            .Pending, .Running => false,
        };
    }
    
    pub fn toString(self: TaskStatus) []const u8 {
        return switch (self) {
            .Pending => "等待中",
            .Running => "运行中",
            .Completed => "已完成",
            .Failed => "失败",
            .Cancelled => "已取消",
        };
    }
};

// ✅ 带值的枚举
const HttpStatusCode = enum(u16) {
    Ok = 200,
    Created = 201,
    NoContent = 204,
    BadRequest = 400,
    Unauthorized = 401,
    Forbidden = 403,
    NotFound = 404,
    InternalServerError = 500,
    
    pub fn isSuccess(self: HttpStatusCode) bool {
        const code = @intFromEnum(self);
        return code >= 200 and code < 300;
    }
    
    pub fn isClientError(self: HttpStatusCode) bool {
        const code = @intFromEnum(self);
        return code >= 400 and code < 500;
    }
    
    pub fn isServerError(self: HttpStatusCode) bool {
        const code = @intFromEnum(self);
        return code >= 500 and code < 600;
    }
};

函数风格

函数签名

zig
const std = @import("std");

// ✅ 好的函数风格
pub fn calculateStatistics(
    allocator: std.mem.Allocator,
    data: []const f64,
    options: StatisticsOptions,
) !Statistics {
    if (data.len == 0) {
        return error.EmptyDataSet;
    }
    
    var sum: f64 = 0;
    var min_val = data[0];
    var max_val = data[0];
    
    // 计算基本统计量
    for (data) |value| {
        sum += value;
        if (value < min_val) min_val = value;
        if (value > max_val) max_val = value;
    }
    
    const mean = sum / @as(f64, @floatFromInt(data.len));
    
    // 计算标准差(如果需要)
    var variance: f64 = 0;
    if (options.calculate_std_dev) {
        for (data) |value| {
            const diff = value - mean;
            variance += diff * diff;
        }
        variance /= @as(f64, @floatFromInt(data.len));
    }
    
    return Statistics{
        .count = data.len,
        .sum = sum,
        .mean = mean,
        .min = min_val,
        .max = max_val,
        .std_dev = if (options.calculate_std_dev) @sqrt(variance) else null,
    };
}

const StatisticsOptions = struct {
    calculate_std_dev: bool = false,
};

const Statistics = struct {
    count: usize,
    sum: f64,
    mean: f64,
    min: f64,
    max: f64,
    std_dev: ?f64,
};

函数组织

zig
const std = @import("std");

// ✅ 按功能组织函数
const StringUtils = struct {
    // 公共 API 在前
    pub fn trim(input: []const u8) []const u8 {
        return trimLeft(trimRight(input));
    }
    
    pub fn split(allocator: std.mem.Allocator, input: []const u8, delimiter: u8) ![][]const u8 {
        var parts = std.ArrayList([]const u8).init(allocator);
        defer parts.deinit();
        
        var start: usize = 0;
        for (input, 0..) |char, i| {
            if (char == delimiter) {
                if (i > start) {
                    try parts.append(input[start..i]);
                }
                start = i + 1;
            }
        }
        
        if (start < input.len) {
            try parts.append(input[start..]);
        }
        
        return parts.toOwnedSlice();
    }
    
    pub fn join(allocator: std.mem.Allocator, parts: []const []const u8, separator: []const u8) ![]u8 {
        if (parts.len == 0) {
            return try allocator.dupe(u8, "");
        }
        
        var total_len: usize = 0;
        for (parts) |part| {
            total_len += part.len;
        }
        total_len += separator.len * (parts.len - 1);
        
        var result = try allocator.alloc(u8, total_len);
        var pos: usize = 0;
        
        for (parts, 0..) |part, i| {
            @memcpy(result[pos..pos + part.len], part);
            pos += part.len;
            
            if (i < parts.len - 1) {
                @memcpy(result[pos..pos + separator.len], separator);
                pos += separator.len;
            }
        }
        
        return result;
    }
    
    // 私有辅助函数在后
    fn trimLeft(input: []const u8) []const u8 {
        var start: usize = 0;
        while (start < input.len and isWhitespace(input[start])) {
            start += 1;
        }
        return input[start..];
    }
    
    fn trimRight(input: []const u8) []const u8 {
        var end = input.len;
        while (end > 0 and isWhitespace(input[end - 1])) {
            end -= 1;
        }
        return input[0..end];
    }
    
    fn isWhitespace(char: u8) bool {
        return char == ' ' or char == '\t' or char == '\n' or char == '\r';
    }
};

测试风格

测试组织

zig
const std = @import("std");
const testing = std.testing;

// 被测试的代码
fn add(a: i32, b: i32) i32 {
    return a + b;
}

fn divide(a: f64, b: f64) !f64 {
    if (b == 0) return error.DivisionByZero;
    return a / b;
}

// ✅ 好的测试风格
test "add function" {
    // 基本情况
    try testing.expect(add(2, 3) == 5);
    try testing.expect(add(-1, 1) == 0);
    try testing.expect(add(0, 0) == 0);
    
    // 边界情况
    try testing.expect(add(std.math.maxInt(i32), 0) == std.math.maxInt(i32));
    try testing.expect(add(std.math.minInt(i32), 0) == std.math.minInt(i32));
}

test "divide function - success cases" {
    // 正常情况
    try testing.expectApproxEqRel(try divide(10.0, 2.0), 5.0, 0.001);
    try testing.expectApproxEqRel(try divide(7.0, 3.0), 2.333333, 0.001);
    
    // 负数
    try testing.expectApproxEqRel(try divide(-10.0, 2.0), -5.0, 0.001);
    try testing.expectApproxEqRel(try divide(10.0, -2.0), -5.0, 0.001);
}

test "divide function - error cases" {
    // 除零错误
    try testing.expectError(error.DivisionByZero, divide(10.0, 0.0));
    try testing.expectError(error.DivisionByZero, divide(-10.0, 0.0));
}

// ✅ 复杂数据结构的测试
test "string utilities" {
    const allocator = testing.allocator;
    
    // 测试分割
    const parts = try StringUtils.split(allocator, "a,b,c", ',');
    defer allocator.free(parts);
    
    try testing.expect(parts.len == 3);
    try testing.expectEqualStrings("a", parts[0]);
    try testing.expectEqualStrings("b", parts[1]);
    try testing.expectEqualStrings("c", parts[2]);
    
    // 测试连接
    const joined = try StringUtils.join(allocator, parts, " | ");
    defer allocator.free(joined);
    
    try testing.expectEqualStrings("a | b | c", joined);
}

// 在前面定义 StringUtils 以供测试使用
const StringUtils = struct {
    pub fn split(allocator: std.mem.Allocator, input: []const u8, delimiter: u8) ![][]const u8 {
        var parts = std.ArrayList([]const u8).init(allocator);
        defer parts.deinit();
        
        var start: usize = 0;
        for (input, 0..) |char, i| {
            if (char == delimiter) {
                if (i > start) {
                    try parts.append(input[start..i]);
                }
                start = i + 1;
            }
        }
        
        if (start < input.len) {
            try parts.append(input[start..]);
        }
        
        return parts.toOwnedSlice();
    }
    
    pub fn join(allocator: std.mem.Allocator, parts: []const []const u8, separator: []const u8) ![]u8 {
        if (parts.len == 0) {
            return try allocator.dupe(u8, "");
        }
        
        var total_len: usize = 0;
        for (parts) |part| {
            total_len += part.len;
        }
        total_len += separator.len * (parts.len - 1);
        
        var result = try allocator.alloc(u8, total_len);
        var pos: usize = 0;
        
        for (parts, 0..) |part, i| {
            @memcpy(result[pos..pos + part.len], part);
            pos += part.len;
            
            if (i < parts.len - 1) {
                @memcpy(result[pos..pos + separator.len], separator);
                pos += separator.len;
            }
        }
        
        return result;
    }
};

性能考虑

内存分配

zig
const std = @import("std");

// ✅ 好的内存管理
pub fn processLargeDataset(allocator: std.mem.Allocator, data: []const u8) ![]u8 {
    // 预估结果大小以减少重新分配
    var result = try std.ArrayList(u8).initCapacity(allocator, data.len * 2);
    defer result.deinit();
    
    // 批量处理以提高效率
    const batch_size = 1024;
    var i: usize = 0;
    
    while (i < data.len) {
        const end = @min(i + batch_size, data.len);
        const batch = data[i..end];
        
        // 处理批次
        try processBatch(&result, batch);
        
        i = end;
    }
    
    return result.toOwnedSlice();
}

fn processBatch(result: *std.ArrayList(u8), batch: []const u8) !void {
    // 批处理逻辑
    for (batch) |byte| {
        try result.append(byte);
        if (byte != 0) {
            try result.append(byte);
        }
    }
}

// ✅ 使用栈分配避免堆分配
pub fn formatSmallString(comptime fmt: []const u8, args: anytype) [256]u8 {
    var buffer: [256]u8 = undefined;
    const result = std.fmt.bufPrint(&buffer, fmt, args) catch "格式化错误";
    
    // 清零剩余部分
    @memset(buffer[result.len..], 0);
    
    return buffer;
}

代码组织

模块结构

zig
//! HTTP 客户端库
//! 
//! 提供简单易用的 HTTP 客户端功能,支持:
//! - GET, POST, PUT, DELETE 请求
//! - 自定义头部
//! - JSON 序列化/反序列化
//! - 连接池
//! - 超时控制

const std = @import("std");

// 公共类型导出
pub const HttpClient = Client;
pub const HttpRequest = Request;
pub const HttpResponse = Response;
pub const HttpError = Error;

// 公共常量
pub const default_timeout_ms = 30000;
pub const max_redirects = 10;

// 错误定义
const Error = error{
    InvalidUrl,
    ConnectionFailed,
    Timeout,
    InvalidResponse,
    TooManyRedirects,
};

// 主要类型定义
const Client = struct {
    allocator: std.mem.Allocator,
    timeout_ms: u32,
    max_redirects: u32,
    
    const Self = @This();
    
    pub fn init(allocator: std.mem.Allocator) Self {
        return Self{
            .allocator = allocator,
            .timeout_ms = default_timeout_ms,
            .max_redirects = max_redirects,
        };
    }
    
    pub fn get(self: *Self, url: []const u8) Error!Response {
        const request = Request{
            .method = .Get,
            .url = url,
            .headers = null,
            .body = null,
        };
        
        return self.send(request);
    }
    
    pub fn post(self: *Self, url: []const u8, body: []const u8) Error!Response {
        const request = Request{
            .method = .Post,
            .url = url,
            .headers = null,
            .body = body,
        };
        
        return self.send(request);
    }
    
    fn send(self: *Self, request: Request) Error!Response {
        // 发送请求的实现
        _ = self;
        _ = request;
        
        return Response{
            .status_code = 200,
            .headers = null,
            .body = "响应体",
        };
    }
};

const Request = struct {
    method: Method,
    url: []const u8,
    headers: ?std.StringHashMap([]const u8),
    body: ?[]const u8,
};

const Response = struct {
    status_code: u16,
    headers: ?std.StringHashMap([]const u8),
    body: []const u8,
};

const Method = enum {
    Get,
    Post,
    Put,
    Delete,
    Patch,
    Head,
    Options,
};

// 测试
test "HTTP client basic functionality" {
    const allocator = std.testing.allocator;
    
    var client = Client.init(allocator);
    const response = try client.get("https://example.com");
    
    std.testing.expect(response.status_code == 200) catch {};
}

工具和自动化

使用 zig fmt

bash
# 格式化单个文件
zig fmt src/main.zig

# 格式化整个目录
zig fmt src/

# 检查格式是否正确(不修改文件)
zig fmt --check src/

# 在构建脚本中添加格式化步骤
zig build fmt

编辑器配置

对于 VS Code,创建 .vscode/settings.json

json
{
    "editor.tabSize": 4,
    "editor.insertSpaces": true,
    "editor.rulers": [100],
    "files.trimTrailingWhitespace": true,
    "files.insertFinalNewline": true,
    "zig.path": "zig",
    "zig.zls.path": "zls",
    "zig.initialLogLevel": "info"
}

总结

本章介绍了 Zig 代码风格的各个方面:

  • ✅ 命名约定:变量、函数、类型、常量的命名规则
  • ✅ 代码格式化:缩进、行长度、空行使用
  • ✅ 注释风格:文档注释、普通注释、顶级注释
  • ✅ 错误处理风格:错误传播和错误集合定义
  • ✅ 结构体和枚举风格:定义和组织方式
  • ✅ 函数风格:签名和组织原则
  • ✅ 测试风格:测试组织和编写方法
  • ✅ 性能考虑:内存管理和优化
  • ✅ 代码组织:模块结构和工具使用

遵循一致的代码风格不仅能提高代码的可读性和可维护性,还能促进团队协作和代码审查的效率。建议在项目中建立并遵循这些风格指南,并使用自动化工具来确保一致性。

本站内容仅供学习和研究使用。