Skip to content

Zig `if` 表达式完全教程

test "if 作为表达式" {
const a: u32 = 10;
const b: u32 = 20;
// 传统方式
const max1 = if (a > b) a else b;
try expect(max1 == 20);
// 更复杂的表达式
const category = if (a < 10) "small"
else if (a < 100) "medium"
else "large";
try expect(std.mem.eql(u8, category, "medium"));
// 嵌套 if 表达式
const result = if (a > b)
"a is greater"
else if (a < b)
"b is greater"
else
"equal";
try expect(std.mem.eql(u8, result, "b is greater"));
}
test "布尔条件" {
const age = 25;
// 基本 if-else
if (age >= 18) {
try expect(true);
} else {
unreachable;
}
// if-else if-else 链
var category: []const u8 = undefined;
if (age < 13) {
category = "child";
} else if (age < 20) {
category = "teenager";
} else if (age < 65) {
category = "adult";
} else {
category = "senior";
}
try expect(std.mem.eql(u8, category, "adult"));
// 无 else 分支
if (age > 30) {
// 30岁以上不会执行
unreachable;
}
}
test "错误联合处理" {
// 可能失败的函数
fn divide(a: i32, b: i32) !i32 {
if (b == 0) return error.DivisionByZero;
return a / b;
}
// 成功情况
const result1 = try divide(10, 2);
try expect(result1 == 5);
// 使用 if 处理错误
const result2 = divide(10, 0);
if (result2) |value| {
// 不会执行(除零错误)
_ = value;
unreachable;
} else |err| {
try expect(err == error.DivisionByZero);
}
// 组合使用
const a: i32 = 20;
const b: i32 = 4;
const result3 = divide(a, b);
if (result3) |value| {
// 处理成功值
const doubled = value * 2;
try expect(doubled == 10);
} else |err| {
// 处理错误
_ = err;
unreachable;
}
}
test "指针捕获错误联合" {
// 可变错误联合
var result: !i32 = 42;
// 通过指针修改值
if (result) |*value| {
value.* = 100; // 修改原值
try expect(value.* == 100);
} else |_| {
unreachable;
}
// 验证修改
if (result) |value| {
try expect(value == 100);
} else |_| {
unreachable;
}
// 错误情况
var error_result: !i32 = error.TestError;
if (error_result) |_| {
unreachable;
} else |err| {
// 错误也是值,可以比较
try expect(err == error.TestError);
}
}
test "可选类型处理" {
// 可能有值的变量
const maybe_number: ?i32 = 42;
const no_number: ?i32 = null;
// 解包可选值
if (maybe_number) |value| {
try expect(value == 42);
} else {
unreachable;
}
// 处理 null 值
if (no_number) |_| {
unreachable;
} else {
try expect(true);
}
// 简写形式(不需要 else)
if (maybe_number) |value| {
const squared = value * value;
try expect(squared == 1764);
}
// 使用相等运算符检查 null
if (no_number == null) {
try expect(true);
}
}
test "可选类型指针捕获" {
// 可变可选类型
var optional_value: ?i32 = 5;
// 通过指针修改
if (optional_value) |*ptr| {
ptr.* = 10; // 修改原值
try expect(ptr.* == 10);
}
// 验证修改
if (optional_value) |value| {
try expect(value == 10);
} else {
unreachable;
}
// 设置为 null
optional_value = null;
if (optional_value) |_| {
unreachable;
} else {
try expect(true);
}
}

4. 组合处理:错误联合 + 可选类型

Section titled “4. 组合处理:错误联合 + 可选类型”
test "错误联合中的可选类型" {
// 可能失败且可能为空的函数
fn parseNumber(str: []const u8) !?i32 {
if (str.len == 0) return null; // 空字符串 -> null
return std.fmt.parseInt(i32, str, 10); // 可能失败
}
// 有效数字
const result1 = parseNumber("42");
if (result1) |optional_value| {
// 解包可选值
if (optional_value) |value| {
try expect(value == 42);
} else {
unreachable;
}
} else |err| {
_ = err;
unreachable;
}
// 空字符串 -> null
const result2 = parseNumber("");
if (result2) |optional_value| {
try expect(optional_value == null);
} else |_| {
unreachable;
}
// 解析失败 -> 错误
const result3 = parseNumber("abc");
if (result3) |optional_value| {
_ = optional_value;
unreachable;
} else |err| {
try expect(err == error.InvalidCharacter);
}
}
test "组合类型的指针捕获" {
// 可变错误联合可选类型
var data: !?i32 = 100;
// 双层指针捕获
if (data) |*optional_ptr| {
// optional_ptr 类型是 *?i32
if (optional_ptr.*) |*value_ptr| {
// value_ptr 类型是 *i32
value_ptr.* = 200;
try expect(value_ptr.* == 200);
} else {
// 处理 null 情况
optional_ptr.* = 300;
}
} else |err| {
_ = err;
unreachable;
}
// 验证修改
if (data) |optional_value| {
try expect(optional_value.? == 200);
} else |_| {
unreachable;
}
}
test "模式匹配" {
const Value = union(enum) {
integer: i32,
float: f64,
text: []const u8,
};
const value = Value{ .integer = 42 };
// 使用 if 模拟模式匹配
if (value == .integer) |int_value| {
try expect(int_value == 42);
} else if (value == .float) |float_value| {
_ = float_value;
unreachable;
} else if (value == .text) |text_value| {
_ = text_value;
unreachable;
}
}
test "链式解包" {
fn getUser() ?struct { name: []const u8, age: u8 } {
return .{ .name = "Alice", .age = 30 };
}
fn getAge(user: anytype) ?u8 {
return user.age;
}
// 链式解包多个可选值
if (getUser()) |user| {
if (getAge(user)) |age| {
try expect(age == 30);
}
}
// 使用 optional 链更简洁
const age = if (getUser()) |user| user.age else null;
try expect(age.? == 30);
}
test "编译期 if" {
comptime {
const debug = true;
// 编译期 if - 只有满足条件的代码会被编译
if (debug) {
const message = "调试模式";
// 这个代码只在 debug 为 true 时编译
// 编译期计算
const length = message.len;
try expect(length == 12);
} else {
// 这个分支不会被编译
@compileError("非调试模式");
}
}
}
test "类型选择的 if" {
// 根据条件选择类型
const use_double = false;
const NumberType = if (use_double) f64 else f32;
var num: NumberType = 3.14;
// 类型在编译时确定
try expect(@TypeOf(num) == f32);
// 函数返回类型也可以条件选择
fn getMultiplier(comptime use_float: bool) type {
return if (use_float) f32 else i32;
}
const Multiplier = getMultiplier(true);
var multiplier: Multiplier = 2.5;
try expect(@TypeOf(multiplier) == f32);
}
const Config = struct {
port: ?u16,
hostname: []const u8,
timeout: u32,
};
fn loadConfig(path: []const u8) !Config {
const file = std.fs.cwd().openFile(path, .{}) catch return error.FileNotFound;
defer file.close();
const content = try file.readToEndAlloc(std.heap.page_allocator, 1024);
defer std.heap.page_allocator.free(content);
// 解析 JSON(可能失败)
const parsed = try std.json.parseFromSlice(Config, std.heap.page_allocator, content);
defer parsed.deinit();
return parsed.value;
}
test "配置处理" {
// 尝试加载配置
const config_result = loadConfig("config.json");
if (config_result) |config| {
// 处理配置
std.debug.print("主机: {s}\n", .{config.hostname});
// 可选字段处理
if (config.port) |port| {
std.debug.print("端口: {}\n", .{port});
} else {
std.debug.print("使用默认端口\n", .{});
}
try expect(config.timeout > 0);
} else |err| {
// 错误处理
switch (err) {
error.FileNotFound => {
std.debug.print("配置文件不存在,使用默认配置\n", .{});
// 使用默认配置继续...
},
error.InvalidJson => {
std.debug.print("配置文件格式错误\n", .{});
return err;
},
else => return err,
}
}
}
const Database = struct {
const Error = error{ ConnectionFailed, QueryFailed, NoResults };
fn connect() Error!*Database {
// 模拟连接
if (std.crypto.random.int(u8) % 2 == 0) {
return error.ConnectionFailed;
}
return @ptrFromInt(0x1234); // 模拟数据库句柄
}
fn query(db: *Database, sql: []const u8) Error!?[]const u8 {
_ = db;
// 模拟查询
if (std.mem.eql(u8, sql, "SELECT * FROM users")) {
return "用户数据";
}
return null; // 无结果
}
};
test "数据库操作" {
// 连接数据库
const db_result = Database.connect();
if (db_result) |db| {
defer {
// 清理资源
// db.close();
}
// 执行查询
const query_result = db.query("SELECT * FROM users");
if (query_result) |maybe_data| {
if (maybe_data) |data| {
// 处理查询结果
try expect(std.mem.eql(u8, data, "用户数据"));
} else {
// 查询成功但无结果
std.debug.print("查询无结果\n", .{});
}
} else |err| {
// 查询失败
std.debug.print("查询失败: {}\n", .{err});
return err;
}
} else |err| {
// 连接失败
std.debug.print("数据库连接失败: {}\n", .{err});
// 使用备用方案...
}
}
// 模式1: 立即处理错误
fn processFile(path: []const u8) !void {
const file = std.fs.cwd().openFile(path, .{}) catch |err| {
std.debug.print("无法打开文件: {s}, 错误: {}\n", .{ path, err });
return err;
};
defer file.close();
// 处理文件...
}
// 模式2: 传播错误并记录
fn processWithLogging() !void {
const result = someOperation();
if (result) |value| {
// 成功处理
return value;
} else |err| {
// 记录错误但继续传播
std.log.err("操作失败: {}", .{err});
return err;
}
}
// 模式3: 提供默认值
fn getConfigValue() ?u32 {
const result = tryLoadConfig();
return if (result) |value| value else 100; // 默认值
}
// 避免深层嵌套
fn processDataBad(data: !?Data) !Result {
if (data) |optional| {
if (optional) |value| {
if (validate(value)) {
return process(value);
} else {
return error.InvalidData;
}
} else {
return error.NoData;
}
} else |err| {
return err;
}
}
// 改进:使用早期返回
fn processDataGood(data: !?Data) !Result {
// 先处理错误
const unwrapped_data = data catch |err| return err;
// 再处理可选值
const value = unwrapped_data orelse return error.NoData;
// 最后处理业务逻辑
if (!validate(value)) return error.InvalidData;
return process(value);
}

Zig 的 if 表达式是一个多功能工具:

  1. 三元表达式替代:简洁的条件赋值
  2. 错误处理:安全地解包错误联合类型
  3. 可选值处理:优雅地处理 null 值
  4. 组合处理:处理复杂的嵌套类型
  5. 编译期决策:在编译时选择代码路径

关键要点:

  • 使用 |value| 语法捕获值
  • 使用 |*ptr| 捕获指针以修改原值
  • else 分支对于错误联合是必需的
  • 可选类型的 else 分支是可选的
  • 组合类型需要逐层解包

通过掌握 if 表达式的各种用法,你可以编写出更安全、更简洁、更易读的 Zig 代码。