Zig 流程控制
流程控制是编程中的核心概念,本章将详细介绍 Zig 中的各种流程控制语句。
if 语句
基本 if 语句
if 语句用于条件判断:
zig
const std = @import("std");
pub fn main() void {
const age = 18;
if (age >= 18) {
std.debug.print("你已经成年了\n", .{});
}
const temperature = 25;
if (temperature > 30) {
std.debug.print("天气很热\n", .{});
} else {
std.debug.print("天气还不错\n", .{});
}
}if-else if 链
zig
const std = @import("std");
pub fn main() void {
const score = 85;
if (score >= 90) {
std.debug.print("优秀\n", .{});
} else if (score >= 80) {
std.debug.print("良好\n", .{});
} else if (score >= 70) {
std.debug.print("中等\n", .{});
} else if (score >= 60) {
std.debug.print("及格\n", .{});
} else {
std.debug.print("不及格\n", .{});
}
}if 表达式
在 Zig 中,if 是表达式,可以返回值:
zig
const std = @import("std");
pub fn main() void {
const x = 10;
const y = 20;
// if 表达式
const max = if (x > y) x else y;
std.debug.print("最大值: {}\n", .{max});
// 复杂的 if 表达式
const status = if (x > 0)
"正数"
else if (x < 0)
"负数"
else
"零";
std.debug.print("数字状态: {s}\n", .{status});
}可选值的 if 处理
if 可以用来处理可选值:
zig
const std = @import("std");
pub fn main() void {
var maybe_number: ?i32 = 42;
// 解包可选值
if (maybe_number) |number| {
std.debug.print("数字是: {}\n", .{number});
} else {
std.debug.print("没有数字\n", .{});
}
// 修改为 null 并再次测试
maybe_number = null;
if (maybe_number) |number| {
std.debug.print("数字是: {}\n", .{number});
} else {
std.debug.print("现在没有数字\n", .{});
}
}错误联合类型的 if 处理
zig
const std = @import("std");
const ParseError = error{
InvalidFormat,
OutOfRange,
};
fn parseNumber(input: []const u8) ParseError!i32 {
if (std.mem.eql(u8, input, "42")) {
return 42;
} else if (std.mem.eql(u8, input, "100")) {
return 100;
} else if (std.mem.eql(u8, input, "999")) {
return ParseError.OutOfRange;
} else {
return ParseError.InvalidFormat;
}
}
pub fn main() void {
const inputs = [_][]const u8{ "42", "100", "999", "abc" };
for (inputs) |input| {
if (parseNumber(input)) |number| {
std.debug.print("解析成功: {} -> {}\n", .{ input, number });
} else |err| {
std.debug.print("解析失败: {} -> {}\n", .{ input, err });
}
}
}switch 语句
基本 switch 语句
switch 语句用于多分支选择:
zig
const std = @import("std");
pub fn main() void {
const day = 3;
switch (day) {
1 => std.debug.print("星期一\n", .{}),
2 => std.debug.print("星期二\n", .{}),
3 => std.debug.print("星期三\n", .{}),
4 => std.debug.print("星期四\n", .{}),
5 => std.debug.print("星期五\n", .{}),
6, 7 => std.debug.print("周末\n", .{}),
else => std.debug.print("无效的日期\n", .{}),
}
}switch 表达式
switch 也可以作为表达式使用:
zig
const std = @import("std");
pub fn main() void {
const grade = 'B';
const score = switch (grade) {
'A' => 90,
'B' => 80,
'C' => 70,
'D' => 60,
'F' => 0,
else => -1,
};
std.debug.print("等级 {} 对应分数: {}\n", .{ grade, score });
}范围匹配
switch 支持范围匹配:
zig
const std = @import("std");
pub fn main() void {
const number = 75;
const category = switch (number) {
0...59 => "不及格",
60...69 => "及格",
70...79 => "中等",
80...89 => "良好",
90...100 => "优秀",
else => "无效分数",
};
std.debug.print("分数 {} 属于: {s}\n", .{ number, category });
}枚举的 switch
switch 与枚举配合使用非常强大:
zig
const std = @import("std");
const Color = enum {
Red,
Green,
Blue,
Yellow,
};
fn getColorName(color: Color) []const u8 {
return switch (color) {
.Red => "红色",
.Green => "绿色",
.Blue => "蓝色",
.Yellow => "黄色",
};
}
pub fn main() void {
const colors = [_]Color{ .Red, .Green, .Blue, .Yellow };
for (colors) |color| {
std.debug.print("颜色: {s}\n", .{getColorName(color)});
}
}联合类型的 switch
switch 可以处理联合类型:
zig
const std = @import("std");
const Value = union(enum) {
Integer: i32,
Float: f64,
String: []const u8,
Boolean: bool,
};
fn processValue(value: Value) void {
switch (value) {
.Integer => |int| std.debug.print("整数: {}\n", .{int}),
.Float => |float| std.debug.print("浮点数: {d:.2}\n", .{float}),
.String => |string| std.debug.print("字符串: {s}\n", .{string}),
.Boolean => |boolean| std.debug.print("布尔值: {}\n", .{boolean}),
}
}
pub fn main() void {
const values = [_]Value{
Value{ .Integer = 42 },
Value{ .Float = 3.14 },
Value{ .String = "Hello" },
Value{ .Boolean = true },
};
for (values) |value| {
processValue(value);
}
}复杂的 switch 模式
zig
const std = @import("std");
const Point = struct {
x: i32,
y: i32,
};
fn analyzePoint(point: Point) []const u8 {
return switch (point) {
.{ .x = 0, .y = 0 } => "原点",
.{ .x = 0, .y = _ } => "Y轴上",
.{ .x = _, .y = 0 } => "X轴上",
.{ .x = var x, .y = var y } => blk: {
if (x == y) {
break :blk "对角线上";
} else if (x > 0 and y > 0) {
break :blk "第一象限";
} else if (x < 0 and y > 0) {
break :blk "第二象限";
} else if (x < 0 and y < 0) {
break :blk "第三象限";
} else {
break :blk "第四象限";
}
},
};
}
pub fn main() void {
const points = [_]Point{
.{ .x = 0, .y = 0 },
.{ .x = 0, .y = 5 },
.{ .x = 3, .y = 0 },
.{ .x = 2, .y = 2 },
.{ .x = 3, .y = 4 },
.{ .x = -2, .y = 3 },
.{ .x = -1, .y = -1 },
.{ .x = 2, .y = -3 },
};
for (points) |point| {
std.debug.print("点 ({}, {}) 位于: {s}\n",
.{ point.x, point.y, analyzePoint(point) });
}
}defer 语句
基本 defer
defer 语句在当前作用域结束时执行:
zig
const std = @import("std");
pub fn main() void {
std.debug.print("开始执行\n", .{});
defer std.debug.print("这会在函数结束时执行\n", .{});
std.debug.print("中间执行\n", .{});
// 函数结束时会执行 defer 语句
}多个 defer 语句
多个 defer 语句按照后进先出(LIFO)的顺序执行:
zig
const std = @import("std");
pub fn main() void {
std.debug.print("开始\n", .{});
defer std.debug.print("第一个 defer\n", .{});
defer std.debug.print("第二个 defer\n", .{});
defer std.debug.print("第三个 defer\n", .{});
std.debug.print("中间\n", .{});
// 执行顺序:第三个 defer -> 第二个 defer -> 第一个 defer
}defer 的实际应用
defer 常用于资源清理:
zig
const std = @import("std");
fn processFile(filename: []const u8) !void {
std.debug.print("打开文件: {s}\n", .{filename});
// 模拟文件操作
defer std.debug.print("关闭文件: {s}\n", .{filename});
std.debug.print("处理文件内容\n", .{});
// 即使发生错误,defer 也会执行
if (std.mem.eql(u8, filename, "error.txt")) {
return error.FileError;
}
std.debug.print("文件处理完成\n", .{});
}
pub fn main() void {
processFile("normal.txt") catch |err| {
std.debug.print("处理文件时出错: {}\n", .{err});
};
std.debug.print("---\n", .{});
processFile("error.txt") catch |err| {
std.debug.print("处理文件时出错: {}\n", .{err});
};
}errdefer 语句
基本 errdefer
errdefer 只在函数返回错误时执行:
zig
const std = @import("std");
const ProcessError = error{
InitializationFailed,
ProcessingFailed,
};
fn processData(should_fail: bool) ProcessError!void {
std.debug.print("开始初始化\n", .{});
errdefer std.debug.print("初始化失败,执行清理\n", .{});
if (should_fail) {
return ProcessError.InitializationFailed;
}
std.debug.print("初始化成功\n", .{});
std.debug.print("处理数据\n", .{});
}
pub fn main() void {
std.debug.print("=== 成功情况 ===\n", .{});
processData(false) catch |err| {
std.debug.print("错误: {}\n", .{err});
};
std.debug.print("\n=== 失败情况 ===\n", .{});
processData(true) catch |err| {
std.debug.print("错误: {}\n", .{err});
};
}errdefer 与 defer 的组合
zig
const std = @import("std");
const ResourceError = error{
AllocationFailed,
InitializationFailed,
};
fn allocateAndInitialize(should_fail_alloc: bool, should_fail_init: bool) ResourceError!void {
std.debug.print("开始分配资源\n", .{});
if (should_fail_alloc) {
return ResourceError.AllocationFailed;
}
// 资源分配成功
defer std.debug.print("释放资源\n", .{});
errdefer std.debug.print("分配成功但初始化失败,释放资源\n", .{});
std.debug.print("开始初始化\n", .{});
if (should_fail_init) {
return ResourceError.InitializationFailed;
}
std.debug.print("初始化成功\n", .{});
}
pub fn main() void {
std.debug.print("=== 分配失败 ===\n", .{});
allocateAndInitialize(true, false) catch |err| {
std.debug.print("错误: {}\n", .{err});
};
std.debug.print("\n=== 初始化失败 ===\n", .{});
allocateAndInitialize(false, true) catch |err| {
std.debug.print("错误: {}\n", .{err});
};
std.debug.print("\n=== 全部成功 ===\n", .{});
allocateAndInitialize(false, false) catch |err| {
std.debug.print("错误: {}\n", .{err});
};
}块表达式
基本块表达式
块可以作为表达式使用:
zig
const std = @import("std");
pub fn main() void {
const result = blk: {
const x = 10;
const y = 20;
break :blk x + y;
};
std.debug.print("块表达式结果: {}\n", .{result});
}复杂的块表达式
zig
const std = @import("std");
fn calculateGrade(score: i32) []const u8 {
return blk: {
if (score >= 90) {
break :blk "A";
} else if (score >= 80) {
break :blk "B";
} else if (score >= 70) {
break :blk "C";
} else if (score >= 60) {
break :blk "D";
} else {
break :blk "F";
}
};
}
pub fn main() void {
const scores = [_]i32{ 95, 85, 75, 65, 55 };
for (scores) |score| {
const grade = calculateGrade(score);
std.debug.print("分数 {} 对应等级: {s}\n", .{ score, grade });
}
}条件编译
comptime if
使用 comptime if 进行条件编译:
zig
const std = @import("std");
const builtin = @import("builtin");
pub fn main() void {
comptime {
if (builtin.os.tag == .windows) {
std.debug.print("编译目标是 Windows\n", .{});
} else if (builtin.os.tag == .linux) {
std.debug.print("编译目标是 Linux\n", .{});
} else if (builtin.os.tag == .macos) {
std.debug.print("编译目标是 macOS\n", .{});
} else {
std.debug.print("编译目标是其他系统\n", .{});
}
}
const is_debug = comptime builtin.mode == .Debug;
if (comptime is_debug) {
std.debug.print("这是调试版本\n", .{});
} else {
std.debug.print("这是发布版本\n", .{});
}
}编译时 switch
zig
const std = @import("std");
fn getOptimizationLevel(comptime mode: std.builtin.Mode) []const u8 {
return comptime switch (mode) {
.Debug => "无优化",
.ReleaseSafe => "安全优化",
.ReleaseFast => "速度优化",
.ReleaseSmall => "大小优化",
};
}
pub fn main() void {
const optimization = comptime getOptimizationLevel(@import("builtin").mode);
std.debug.print("当前优化级别: {s}\n", .{optimization});
}流程控制的最佳实践
1. 保持条件简洁
zig
const std = @import("std");
pub fn main() void {
const user_age = 25;
const has_license = true;
const has_insurance = true;
// ✅ 好的做法:条件清晰
const can_drive = user_age >= 18 and has_license and has_insurance;
if (can_drive) {
std.debug.print("可以开车\n", .{});
} else {
std.debug.print("不能开车\n", .{});
}
}2. 优先使用 switch 而不是长的 if-else 链
zig
const std = @import("std");
const Status = enum {
Pending,
Processing,
Completed,
Failed,
};
pub fn main() void {
const status = Status.Processing;
// ✅ 好的做法:使用 switch
const message = switch (status) {
.Pending => "等待中",
.Processing => "处理中",
.Completed => "已完成",
.Failed => "失败",
};
std.debug.print("状态: {s}\n", .{message});
}3. 合理使用 defer 进行资源管理
zig
const std = @import("std");
fn processWithCleanup() !void {
std.debug.print("获取资源\n", .{});
// ✅ 好的做法:立即设置清理代码
defer std.debug.print("释放资源\n", .{});
std.debug.print("使用资源\n", .{});
// 无论函数如何退出,资源都会被释放
}
pub fn main() void {
processWithCleanup() catch {};
}实践练习
练习 1:成绩管理系统
zig
const std = @import("std");
const Student = struct {
name: []const u8,
score: i32,
};
pub fn main() void {
const students = [_]Student{
.{ .name = "张三", .score = 85 },
.{ .name = "李四", .score = 92 },
.{ .name = "王五", .score = 78 },
.{ .name = "赵六", .score = 65 },
};
// TODO: 使用流程控制语句完成以下任务:
// 1. 为每个学生分配等级 (A: 90+, B: 80-89, C: 70-79, D: 60-69, F: <60)
// 2. 统计各等级的人数
// 3. 找出最高分和最低分的学生
}练习 2:简单计算器
zig
const std = @import("std");
const Operation = enum {
Add,
Subtract,
Multiply,
Divide,
};
pub fn main() void {
// TODO: 实现一个简单的计算器
// 1. 使用 switch 处理不同的运算操作
// 2. 处理除零错误
// 3. 使用 defer 进行资源清理
}练习 3:状态机
zig
const std = @import("std");
const State = enum {
Idle,
Running,
Paused,
Stopped,
};
const Event = enum {
Start,
Pause,
Resume,
Stop,
};
pub fn main() void {
// TODO: 实现一个简单的状态机
// 1. 根据当前状态和事件,决定下一个状态
// 2. 使用 switch 处理状态转换
// 3. 处理无效的状态转换
}总结
本章详细介绍了 Zig 的流程控制语句:
- ✅
if语句:条件判断,支持表达式形式 - ✅
switch语句:多分支选择,功能强大 - ✅
defer和errdefer:资源管理和清理 - ✅ 块表达式:将代码块作为表达式使用
- ✅ 条件编译:编译时流程控制
- ✅ 最佳实践和实际应用
掌握流程控制是编写复杂程序逻辑的基础。在下一章中,我们将学习 Zig 的错误处理机制。