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 代码风格的各个方面:
- ✅ 命名约定:变量、函数、类型、常量的命名规则
- ✅ 代码格式化:缩进、行长度、空行使用
- ✅ 注释风格:文档注释、普通注释、顶级注释
- ✅ 错误处理风格:错误传播和错误集合定义
- ✅ 结构体和枚举风格:定义和组织方式
- ✅ 函数风格:签名和组织原则
- ✅ 测试风格:测试组织和编写方法
- ✅ 性能考虑:内存管理和优化
- ✅ 代码组织:模块结构和工具使用
遵循一致的代码风格不仅能提高代码的可读性和可维护性,还能促进团队协作和代码审查的效率。建议在项目中建立并遵循这些风格指南,并使用自动化工具来确保一致性。