Zig 结构体和枚举
结构体和枚举是 Zig 中用于组织和表示复杂数据的重要工具。本章将详细介绍它们的定义、使用和最佳实践。
结构体基础
结构体定义和初始化
zig
const std = @import("std");
// 基本结构体定义
const Point = struct {
x: f32,
y: f32,
};
// 带默认值的结构体
const Rectangle = struct {
width: f32 = 1.0,
height: f32 = 1.0,
x: f32 = 0.0,
y: f32 = 0.0,
};
// 复杂结构体
const Person = struct {
name: []const u8,
age: u32,
height: f32,
is_student: bool,
};
pub fn main() void {
// 结构体初始化
const point1 = Point{ .x = 3.0, .y = 4.0 };
const point2 = Point{ .y = 2.0, .x = 1.0 }; // 字段顺序可以不同
std.debug.print("点1: ({d:.1}, {d:.1})\n", .{ point1.x, point1.y });
std.debug.print("点2: ({d:.1}, {d:.1})\n", .{ point2.x, point2.y });
// 使用默认值
const rect1 = Rectangle{};
const rect2 = Rectangle{ .width = 5.0, .height = 3.0 };
std.debug.print("矩形1: {}x{} at ({d:.1}, {d:.1})\n",
.{ rect1.width, rect1.height, rect1.x, rect1.y });
std.debug.print("矩形2: {}x{} at ({d:.1}, {d:.1})\n",
.{ rect2.width, rect2.height, rect2.x, rect2.y });
// 复杂结构体
const person = Person{
.name = "张三",
.age = 25,
.height = 175.5,
.is_student = true,
};
std.debug.print("人员信息: {s}, {} 岁, {d:.1}cm, 学生: {}\n",
.{ person.name, person.age, person.height, person.is_student });
}结构体方法
zig
const std = @import("std");
const Circle = struct {
center: Point,
radius: f32,
const Self = @This();
// 构造函数
pub fn init(x: f32, y: f32, radius: f32) Self {
return Self{
.center = Point{ .x = x, .y = y },
.radius = radius,
};
}
// 计算面积
pub fn area(self: Self) f32 {
return std.math.pi * self.radius * self.radius;
}
// 计算周长
pub fn circumference(self: Self) f32 {
return 2.0 * std.math.pi * self.radius;
}
// 检查点是否在圆内
pub fn contains(self: Self, point: Point) bool {
const dx = point.x - self.center.x;
const dy = point.y - self.center.y;
const distance_squared = dx * dx + dy * dy;
return distance_squared <= self.radius * self.radius;
}
// 移动圆心
pub fn move(self: *Self, dx: f32, dy: f32) void {
self.center.x += dx;
self.center.y += dy;
}
// 缩放半径
pub fn scale(self: *Self, factor: f32) void {
self.radius *= factor;
}
};
const Point = struct {
x: f32,
y: f32,
pub fn distance(self: Point, other: Point) f32 {
const dx = self.x - other.x;
const dy = self.y - other.y;
return @sqrt(dx * dx + dy * dy);
}
};
pub fn main() void {
// 创建圆
var circle = Circle.init(0.0, 0.0, 5.0);
std.debug.print("圆心: ({d:.1}, {d:.1}), 半径: {d:.1}\n",
.{ circle.center.x, circle.center.y, circle.radius });
std.debug.print("面积: {d:.2}\n", .{circle.area()});
std.debug.print("周长: {d:.2}\n", .{circle.circumference()});
// 测试点是否在圆内
const test_points = [_]Point{
Point{ .x = 0.0, .y = 0.0 },
Point{ .x = 3.0, .y = 4.0 },
Point{ .x = 6.0, .y = 0.0 },
};
for (test_points) |point| {
const inside = circle.contains(point);
std.debug.print("点 ({d:.1}, {d:.1}) 在圆内: {}\n",
.{ point.x, point.y, inside });
}
// 移动和缩放圆
circle.move(2.0, 3.0);
circle.scale(1.5);
std.debug.print("移动和缩放后:\n");
std.debug.print("圆心: ({d:.1}, {d:.1}), 半径: {d:.1}\n",
.{ circle.center.x, circle.center.y, circle.radius });
}嵌套结构体
zig
const std = @import("std");
const Address = struct {
street: []const u8,
city: []const u8,
postal_code: []const u8,
country: []const u8,
pub fn format(self: Address) void {
std.debug.print("地址: {s}, {s}, {s}, {s}\n",
.{ self.street, self.city, self.postal_code, self.country });
}
};
const Contact = struct {
phone: []const u8,
email: []const u8,
pub fn format(self: Contact) void {
std.debug.print("联系方式: 电话 {s}, 邮箱 {s}\n", .{ self.phone, self.email });
}
};
const Employee = struct {
id: u32,
name: []const u8,
department: []const u8,
salary: f64,
address: Address,
contact: Contact,
const Self = @This();
pub fn init(id: u32, name: []const u8, department: []const u8, salary: f64) Self {
return Self{
.id = id,
.name = name,
.department = department,
.salary = salary,
.address = Address{
.street = "",
.city = "",
.postal_code = "",
.country = "",
},
.contact = Contact{
.phone = "",
.email = "",
},
};
}
pub fn setAddress(self: *Self, address: Address) void {
self.address = address;
}
pub fn setContact(self: *Self, contact: Contact) void {
self.contact = contact;
}
pub fn displayInfo(self: Self) void {
std.debug.print("员工信息:\n");
std.debug.print(" ID: {}\n", .{self.id});
std.debug.print(" 姓名: {s}\n", .{self.name});
std.debug.print(" 部门: {s}\n", .{self.department});
std.debug.print(" 薪资: {d:.2}\n", .{self.salary});
std.debug.print(" ");
self.address.format();
std.debug.print(" ");
self.contact.format();
}
};
pub fn main() void {
var employee = Employee.init(1001, "李四", "技术部", 8500.0);
const address = Address{
.street = "中关村大街1号",
.city = "北京",
.postal_code = "100080",
.country = "中国",
};
const contact = Contact{
.phone = "138-0000-0000",
.email = "lisi@company.com",
};
employee.setAddress(address);
employee.setContact(contact);
employee.displayInfo();
}枚举基础
简单枚举
zig
const std = @import("std");
// 基本枚举
const Color = enum {
Red,
Green,
Blue,
Yellow,
Purple,
// 枚举方法
pub fn toString(self: Color) []const u8 {
return switch (self) {
.Red => "红色",
.Green => "绿色",
.Blue => "蓝色",
.Yellow => "黄色",
.Purple => "紫色",
};
}
pub fn isWarm(self: Color) bool {
return switch (self) {
.Red, .Yellow => true,
.Green, .Blue, .Purple => false,
};
}
};
// 带值的枚举
const HttpStatus = enum(u16) {
Ok = 200,
NotFound = 404,
InternalServerError = 500,
BadRequest = 400,
Unauthorized = 401,
pub fn getMessage(self: HttpStatus) []const u8 {
return switch (self) {
.Ok => "请求成功",
.NotFound => "资源未找到",
.InternalServerError => "服务器内部错误",
.BadRequest => "请求格式错误",
.Unauthorized => "未授权访问",
};
}
pub fn isError(self: HttpStatus) bool {
return @intFromEnum(self) >= 400;
}
};
pub fn main() void {
// 使用基本枚举
const colors = [_]Color{ .Red, .Green, .Blue, .Yellow, .Purple };
std.debug.print("颜色信息:\n");
for (colors) |color| {
std.debug.print(" {s} - 暖色调: {}\n", .{ color.toString(), color.isWarm() });
}
// 使用带值的枚举
const statuses = [_]HttpStatus{ .Ok, .NotFound, .InternalServerError, .BadRequest };
std.debug.print("\nHTTP 状态码:\n");
for (statuses) |status| {
std.debug.print(" {} - {s} - 错误: {}\n",
.{ @intFromEnum(status), status.getMessage(), status.isError() });
}
}枚举与 switch
zig
const std = @import("std");
const Operation = enum {
Add,
Subtract,
Multiply,
Divide,
pub fn calculate(self: Operation, a: f64, b: f64) !f64 {
return switch (self) {
.Add => a + b,
.Subtract => a - b,
.Multiply => a * b,
.Divide => {
if (b == 0) return error.DivisionByZero;
return a / b;
},
};
}
pub fn getSymbol(self: Operation) []const u8 {
return switch (self) {
.Add => "+",
.Subtract => "-",
.Multiply => "*",
.Divide => "/",
};
}
};
const Priority = enum {
Low,
Medium,
High,
Critical,
pub fn getLevel(self: Priority) u8 {
return switch (self) {
.Low => 1,
.Medium => 2,
.High => 3,
.Critical => 4,
};
}
pub fn getColor(self: Priority) []const u8 {
return switch (self) {
.Low => "绿色",
.Medium => "黄色",
.High => "橙色",
.Critical => "红色",
};
}
};
pub fn main() void {
// 计算器示例
const operations = [_]Operation{ .Add, .Subtract, .Multiply, .Divide };
const a: f64 = 10.0;
const b: f64 = 3.0;
std.debug.print("计算器 ({d:.1} 和 {d:.1}):\n", .{ a, b });
for (operations) |op| {
if (op.calculate(a, b)) |result| {
std.debug.print(" {d:.1} {} {d:.1} = {d:.2}\n",
.{ a, op.getSymbol(), b, result });
} else |err| {
std.debug.print(" {d:.1} {} {d:.1} = 错误: {}\n",
.{ a, op.getSymbol(), b, err });
}
}
// 优先级示例
const priorities = [_]Priority{ .Low, .Medium, .High, .Critical };
std.debug.print("\n优先级系统:\n");
for (priorities) |priority| {
std.debug.print(" {} 级 - 颜色: {s}\n",
.{ priority.getLevel(), priority.getColor() });
}
}联合类型 (Union)
标记联合
zig
const std = @import("std");
// 标记联合:可以存储不同类型的值
const Value = union(enum) {
Integer: i32,
Float: f64,
String: []const u8,
Boolean: bool,
Array: []i32,
pub fn getType(self: Value) []const u8 {
return switch (self) {
.Integer => "整数",
.Float => "浮点数",
.String => "字符串",
.Boolean => "布尔值",
.Array => "数组",
};
}
pub fn print(self: Value) void {
switch (self) {
.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}),
.Array => |array| {
std.debug.print("数组: [");
for (array, 0..) |item, i| {
if (i > 0) std.debug.print(", ");
std.debug.print("{}", .{item});
}
std.debug.print("]\n");
},
}
}
};
// 表达式求值器
const Expression = union(enum) {
Number: f64,
Add: struct { left: *Expression, right: *Expression },
Subtract: struct { left: *Expression, right: *Expression },
Multiply: struct { left: *Expression, right: *Expression },
Divide: struct { left: *Expression, right: *Expression },
pub fn evaluate(self: Expression, allocator: std.mem.Allocator) !f64 {
_ = allocator; // 这里简化处理,实际使用中可能需要分配内存
return switch (self) {
.Number => |num| num,
.Add => |add| try add.left.evaluate(allocator) + try add.right.evaluate(allocator),
.Subtract => |sub| try sub.left.evaluate(allocator) - try sub.right.evaluate(allocator),
.Multiply => |mul| try mul.left.evaluate(allocator) * try mul.right.evaluate(allocator),
.Divide => |div| {
const right_val = try div.right.evaluate(allocator);
if (right_val == 0) return error.DivisionByZero;
return try div.left.evaluate(allocator) / right_val;
},
};
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// 值联合示例
const values = [_]Value{
Value{ .Integer = 42 },
Value{ .Float = 3.14159 },
Value{ .String = "Hello, Zig!" },
Value{ .Boolean = true },
Value{ .Array = &[_]i32{ 1, 2, 3, 4, 5 } },
};
std.debug.print("值联合示例:\n");
for (values) |value| {
std.debug.print("类型: {s} - ", .{value.getType()});
value.print();
}
// 表达式求值示例 (简化版本)
const num1 = Expression{ .Number = 10.0 };
const num2 = Expression{ .Number = 5.0 };
std.debug.print("\n简单表达式求值:\n");
std.debug.print("10.0 = {d:.1}\n", .{try num1.evaluate(allocator)});
std.debug.print("5.0 = {d:.1}\n", .{try num2.evaluate(allocator)});
}可选类型与结构体
可选字段
zig
const std = @import("std");
const User = struct {
id: u32,
username: []const u8,
email: ?[]const u8, // 可选字段
phone: ?[]const u8, // 可选字段
age: ?u32, // 可选字段
const Self = @This();
pub fn init(id: u32, username: []const u8) Self {
return Self{
.id = id,
.username = username,
.email = null,
.phone = null,
.age = null,
};
}
pub fn setEmail(self: *Self, email: []const u8) void {
self.email = email;
}
pub fn setPhone(self: *Self, phone: []const u8) void {
self.phone = phone;
}
pub fn setAge(self: *Self, age: u32) void {
self.age = age;
}
pub fn displayInfo(self: Self) void {
std.debug.print("用户信息:\n");
std.debug.print(" ID: {}\n", .{self.id});
std.debug.print(" 用户名: {s}\n", .{self.username});
if (self.email) |email| {
std.debug.print(" 邮箱: {s}\n", .{email});
} else {
std.debug.print(" 邮箱: 未设置\n", .{});
}
if (self.phone) |phone| {
std.debug.print(" 电话: {s}\n", .{phone});
} else {
std.debug.print(" 电话: 未设置\n", .{});
}
if (self.age) |age| {
std.debug.print(" 年龄: {}\n", .{age});
} else {
std.debug.print(" 年龄: 未设置\n", .{});
}
}
pub fn hasCompleteProfile(self: Self) bool {
return self.email != null and self.phone != null and self.age != null;
}
};
pub fn main() void {
var user1 = User.init(1001, "zhangsan");
user1.setEmail("zhangsan@example.com");
user1.setAge(25);
var user2 = User.init(1002, "lisi");
user2.setEmail("lisi@example.com");
user2.setPhone("138-0000-0000");
user2.setAge(30);
std.debug.print("用户1:\n");
user1.displayInfo();
std.debug.print("完整资料: {}\n\n", .{user1.hasCompleteProfile()});
std.debug.print("用户2:\n");
user2.displayInfo();
std.debug.print("完整资料: {}\n", .{user2.hasCompleteProfile()});
}泛型结构体
泛型容器
zig
const std = @import("std");
// 泛型栈
fn Stack(comptime T: type) type {
return struct {
items: []T,
count: usize,
allocator: std.mem.Allocator,
const Self = @This();
pub fn init(allocator: std.mem.Allocator, capacity: usize) !Self {
const items = try allocator.alloc(T, capacity);
return Self{
.items = items,
.count = 0,
.allocator = allocator,
};
}
pub fn deinit(self: *Self) void {
self.allocator.free(self.items);
}
pub fn push(self: *Self, item: T) !void {
if (self.count >= self.items.len) {
return error.StackOverflow;
}
self.items[self.count] = item;
self.count += 1;
}
pub fn pop(self: *Self) ?T {
if (self.count == 0) return null;
self.count -= 1;
return self.items[self.count];
}
pub fn peek(self: *const Self) ?T {
if (self.count == 0) return null;
return self.items[self.count - 1];
}
pub fn isEmpty(self: *const Self) bool {
return self.count == 0;
}
pub fn size(self: *const Self) usize {
return self.count;
}
};
}
// 泛型键值对
fn KeyValue(comptime K: type, comptime V: type) type {
return struct {
key: K,
value: V,
const Self = @This();
pub fn init(key: K, value: V) Self {
return Self{ .key = key, .value = value };
}
pub fn format(self: Self) void {
std.debug.print("({}, {})", .{ self.key, self.value });
}
};
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// 整数栈
var int_stack = try Stack(i32).init(allocator, 10);
defer int_stack.deinit();
// 推入元素
try int_stack.push(10);
try int_stack.push(20);
try int_stack.push(30);
std.debug.print("整数栈大小: {}\n", .{int_stack.size()});
std.debug.print("栈顶元素: {?}\n", .{int_stack.peek()});
// 弹出元素
while (!int_stack.isEmpty()) {
if (int_stack.pop()) |item| {
std.debug.print("弹出: {}\n", .{item});
}
}
// 字符串栈
var string_stack = try Stack([]const u8).init(allocator, 5);
defer string_stack.deinit();
try string_stack.push("第一个");
try string_stack.push("第二个");
try string_stack.push("第三个");
std.debug.print("\n字符串栈:\n");
while (!string_stack.isEmpty()) {
if (string_stack.pop()) |item| {
std.debug.print("弹出: {s}\n", .{item});
}
}
// 键值对
const IntStringPair = KeyValue(i32, []const u8);
const pairs = [_]IntStringPair{
IntStringPair.init(1, "一"),
IntStringPair.init(2, "二"),
IntStringPair.init(3, "三"),
};
std.debug.print("\n键值对:\n");
for (pairs) |pair| {
std.debug.print(" ");
pair.format();
std.debug.print("\n");
}
}结构体和枚举的最佳实践
1. 合理的结构设计
zig
const std = @import("std");
// ✅ 好的设计:职责单一,字段相关
const BankAccount = struct {
account_number: []const u8,
balance: f64,
account_type: AccountType,
is_active: bool,
const Self = @This();
pub fn init(account_number: []const u8, account_type: AccountType) Self {
return Self{
.account_number = account_number,
.balance = 0.0,
.account_type = account_type,
.is_active = true,
};
}
pub fn deposit(self: *Self, amount: f64) !void {
if (!self.is_active) return error.AccountInactive;
if (amount <= 0) return error.InvalidAmount;
self.balance += amount;
}
pub fn withdraw(self: *Self, amount: f64) !void {
if (!self.is_active) return error.AccountInactive;
if (amount <= 0) return error.InvalidAmount;
if (amount > self.balance) return error.InsufficientFunds;
self.balance -= amount;
}
pub fn getBalance(self: *const Self) f64 {
return self.balance;
}
};
const AccountType = enum {
Checking,
Savings,
Investment,
pub fn getInterestRate(self: AccountType) f64 {
return switch (self) {
.Checking => 0.001,
.Savings => 0.025,
.Investment => 0.05,
};
}
};
pub fn main() !void {
var account = BankAccount.init("123456789", .Savings);
try account.deposit(1000.0);
std.debug.print("存款后余额: {d:.2}\n", .{account.getBalance()});
try account.withdraw(250.0);
std.debug.print("取款后余额: {d:.2}\n", .{account.getBalance()});
const interest_rate = account.account_type.getInterestRate();
std.debug.print("利率: {d:.3}%\n", .{interest_rate * 100});
}2. 错误处理集成
zig
const std = @import("std");
const ValidationError = error{
InvalidEmail,
InvalidPhone,
InvalidAge,
EmptyName,
};
const ContactInfo = struct {
name: []const u8,
email: []const u8,
phone: []const u8,
age: u32,
const Self = @This();
pub fn validate(self: Self) ValidationError!void {
// 验证姓名
if (self.name.len == 0) {
return ValidationError.EmptyName;
}
// 验证邮箱
if (std.mem.indexOf(u8, self.email, "@") == null) {
return ValidationError.InvalidEmail;
}
// 验证电话
if (self.phone.len < 10) {
return ValidationError.InvalidPhone;
}
// 验证年龄
if (self.age < 18 or self.age > 120) {
return ValidationError.InvalidAge;
}
}
pub fn create(name: []const u8, email: []const u8, phone: []const u8, age: u32) ValidationError!Self {
const contact = Self{
.name = name,
.email = email,
.phone = phone,
.age = age,
};
try contact.validate();
return contact;
}
};
pub fn main() void {
const test_contacts = [_]struct {
name: []const u8,
email: []const u8,
phone: []const u8,
age: u32,
}{
.{ .name = "张三", .email = "zhangsan@example.com", .phone = "13800000000", .age = 25 },
.{ .name = "", .email = "invalid-email", .phone = "123", .age = 200 },
.{ .name = "李四", .email = "lisi@example.com", .phone = "13900000000", .age = 30 },
};
for (test_contacts, 0..) |contact_data, i| {
std.debug.print("测试联系人 {}:\n", .{i + 1});
if (ContactInfo.create(contact_data.name, contact_data.email, contact_data.phone, contact_data.age)) |contact| {
std.debug.print(" ✅ 验证通过: {s}\n", .{contact.name});
} else |err| {
std.debug.print(" ❌ 验证失败: {}\n", .{err});
}
}
}3. 内存效率
zig
const std = @import("std");
// ✅ 内存对齐优化
const OptimizedStruct = struct {
// 按大小排序字段以减少填充
large_field: u64, // 8 字节
medium_field: u32, // 4 字节
small_field1: u16, // 2 字节
small_field2: u16, // 2 字节
tiny_field1: u8, // 1 字节
tiny_field2: u8, // 1 字节
tiny_field3: u8, // 1 字节
tiny_field4: u8, // 1 字节
flag: bool, // 1 字节
// 总共: 24 字节 (优化后)
};
// ❌ 未优化的结构体
const UnoptimizedStruct = struct {
flag: bool, // 1 字节 + 7 字节填充
large_field: u64, // 8 字节
tiny_field1: u8, // 1 字节 + 3 字节填充
medium_field: u32, // 4 字节
tiny_field2: u8, // 1 字节 + 1 字节填充
small_field1: u16, // 2 字节
tiny_field3: u8, // 1 字节 + 1 字节填充
small_field2: u16, // 2 字节
tiny_field4: u8, // 1 字节 + 7 字节填充
// 总共: 40 字节 (未优化)
};
pub fn main() void {
std.debug.print("结构体大小比较:\n");
std.debug.print("优化后: {} 字节\n", .{@sizeOf(OptimizedStruct)});
std.debug.print("未优化: {} 字节\n", .{@sizeOf(UnoptimizedStruct)});
std.debug.print("节省: {} 字节\n", .{@sizeOf(UnoptimizedStruct) - @sizeOf(OptimizedStruct)});
// 对齐信息
std.debug.print("\n对齐要求:\n");
std.debug.print("OptimizedStruct: {} 字节\n", .{@alignOf(OptimizedStruct)});
std.debug.print("UnoptimizedStruct: {} 字节\n", .{@alignOf(UnoptimizedStruct)});
}总结
本章详细介绍了 Zig 的结构体和枚举:
- ✅ 结构体的定义、初始化和方法
- ✅ 嵌套结构体和复杂数据组织
- ✅ 枚举的基本使用和带值枚举
- ✅ 联合类型和标记联合
- ✅ 可选类型与结构体的结合
- ✅ 泛型结构体的实现
- ✅ 最佳实践和内存优化
结构体和枚举是构建复杂数据结构和实现面向对象设计的基础工具。掌握它们的使用对于编写高质量的 Zig 程序至关重要。在下一章中,我们将学习 Zig 的原子操作。