Zig 基本语法

Zig 是一种现代系统编程语言,语法简洁、明确,旨在提供高性能、安全性和易维护性。它的语法受到 C 和 Rust 的启发,但去除了复杂特性(如宏、操作符重载),并通过编译时检查增强安全性。以下是对 Zig 基本语法的中文讲解,涵盖变量、数据类型、控制流、函数、错误处理等核心内容,结合示例代码,力求简洁清晰,基于 Zig 0.14.1(截至 2025 年 5 月的稳定版)。


1. 基本概念

  • 静态类型:Zig 是强类型语言,变量类型在编译时确定,支持类型推断。
  • 无隐藏控制流:所有操作显式,避免隐式行为。
  • 编译时执行:通过 comptime 支持编译时计算,优化性能。
  • 无垃圾回收:手动管理内存,使用分配器(allocator)。

2. 变量与常量

Zig 使用 var 声明变量,const 声明常量,comptime 用于编译时常量。

示例

const std = @import("std");

pub fn main() !void {
    // 常量(不可变)
    const a: i32 = 10;
    // 变量(可变)
    var b: i32 = 20;
    b += a;

    // 类型推断
    const c = 30; // 自动推断为 i32
    var d = b + c; // 自动推断为 i32

    // 编译时常量
    comptime var result = a + b;
    const stdout = std.io.getStdOut().writer();
    try stdout.print("结果: {}\n", .{result}); // 输出:结果: 30
}

说明

  • const:值不可修改,编译时确定。
  • var:值可修改,需显式指定类型或通过初始值推断。
  • 类型后缀(如 i32):明确变量类型。
  • comptime:确保表达式在编译时计算。

3. 基本数据类型

Zig 提供丰富的内置类型,类型名称简洁。

基本类型

  • 整数
  • 有符号:i8, i16, i32, i64, i128
  • 无符号:u8, u16, u32, u64, u128
  • 任意大小:isize, usize(依赖平台)。
  • 浮点数f32, f64
  • 布尔值booltruefalse)。
  • 空类型void
  • 可选类型?T(表示可能为 null)。
  var optional: ?i32 = null;
  • 错误联合!T(表示可能返回错误)。
  • 指针
  • 单元素:*T
  • 多元素:[*]T, []T(切片,需指定长度或运行时确定)。
  • 数组[N]T(固定长度)。
  var arr: [3]i32 = [_]i32{1, 2, 3};

示例

const std = @import("std");

pub fn main() !void {
    var num: u32 = 42;
    var optional: ?f32 = 3.14;
    var arr: [2]u8 = [_]u8{10, 20};

    const stdout = std.io.getStdOut().writer();
    try stdout.print("无符号: {}, 可选: {}, 数组: {}\n", .{num, optional orelse 0.0, arr[0]});
}

输出

无符号: 42, 可选: 3.14, 数组: 10

说明

  • orelse:处理可选类型的默认值。
  • [_]T{...}:数组初始化,_ 自动推断长度。

4. 控制流

Zig 支持常见的控制结构,语法简洁。

条件语句

  • if:支持条件分支,可处理可选类型和错误。
  var x: ?i32 = 10;
  if (x) |value| {
      std.debug.print("值: {}\n", .{value}); // 输出:值: 10
  } else {
      std.debug.print("为空\n", .{});
  }
  • 比较运算符==, !=, <, >, <=, >=
  • 逻辑运算符and, or, !

循环

  • for:迭代数组、切片等。
  const arr = [_]u8{1, 2, 3};
  for (arr) |item| {
      std.debug.print("项: {}\n", .{item});
  }
  • while:支持条件循环。
  var i: u32 = 0;
  while (i < 3) : (i += 1) {
      std.debug.print("计数: {}\n", .{i});
  }

输出

项: 1
项: 2
项: 3
计数: 0
计数: 1
计数: 2

说明

  • for 捕获值使用 |value|
  • while: (i += 1) 是继续表达式,简化循环更新。

Switch

类似 C 的 switch,但更灵活,支持范围和枚举。

var x: u8 = 2;
switch (x) {
    1 => std.debug.print("一\n", .{}),
    2, 3 => std.debug.print("二或三\n", .{}),
    else => std.debug.print("其他\n", .{}),
}

输出

二或三

5. 函数

Zig 的函数定义使用 fn,支持返回值、错误处理和编译时参数。

基本函数

fn add(a: i32, b: i32) i32 {
    return a + b;
}

pub fn main() !void {
    const result = add(5, 3);
    std.debug.print("加法: {}\n", .{result}); // 输出:加法: 8
}

错误处理

使用 !T 表示可能返回错误。

const MyError = error{InvalidInput};

fn divide(a: f32, b: f32) MyError!f32 {
    if (b == 0) return MyError.InvalidInput;
    return a / b;
}

pub fn main() !void {
    const result = divide(10.0, 2.0) catch |err| {
        std.debug.print("错误: {}\n", .{err});
        return;
    };
    std.debug.print("除法: {}\n", .{result}); // 输出:除法: 5.0
}

说明

  • try:尝试调用可能出错的函数,失败时返回错误。
  • catch:捕获错误并处理。

6. 内存管理

Zig 没有垃圾回收,依赖分配器管理内存。

示例

const std = @import("std");

pub fn main() !void {
    const allocator = std.heap.page_allocator;
    var list = try std.ArrayList(u32).init(allocator);
    defer list.deinit(); // 确保释放内存

    try list.append(42);
    std.debug.print("列表: {}\n", .{list.items[0]}); // 输出:列表: 42
}

说明

  • std.ArrayList:动态数组,需分配器。
  • defer:在函数退出时执行清理。
  • try:处理内存分配错误。

7. 结构体与枚举

  • 结构体:定义自定义数据类型。
  const Point = struct {
      x: f32,
      y: f32,

      fn distance(self: Point) f32 {
          return @sqrt(self.x * self.x + self.y * self.y);
      }
  };

  pub fn main() !void {
      const p = Point{ .x = 3.0, .y = 4.0 };
      std.debug.print("距离: {}\n", .{p.distance()}); // 输出:距离: 5.0
  }
  • 枚举:定义有限值集合。
  const Color = enum { Red, Green, Blue };

  pub fn main() !void {
      const c = Color.Green;
      std.debug.print("颜色: {}\n", .{c}); // 输出:颜色: Green
  }

8. 注意事项

  • 类型安全
  • 显式类型声明或推断,避免类型错误。
  • 可选类型(?T)和错误联合(!T)防止未定义行为。
  • 编译时优化
  • 使用 comptime 减少运行时开销。
  • 避免复杂编译时逻辑,保持代码清晰。
  • 错误处理
  • 所有可能失败的操作需显式处理(trycatch)。
  • 定义清晰的错误集(如 error{...})。
  • 调试
  • 使用 std.debug.print 输出调试信息。
  • 编译时启用调试:zig build-exe -g
  • 性能
  • Zig 编译为本地机器码,性能接近 C。
  • 手动内存管理需小心,避免泄漏。

9. 综合示例

以下是一个综合示例,展示变量、控制流、函数和错误处理:

const std = @import("std");

const MyError = error{OutOfRange};

fn checkAge(age: u8) MyError!u8 {
    if (age < 18 or age > 100) return MyError.OutOfRange;
    return age;
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    const ages = [_]u8{15, 25, 120};

    for (ages) |age| {
        const result = checkAge(age) catch |err| {
            try stdout.print("年龄 {} 错误: {}\n", .{age, err});
            continue;
        };
        try stdout.print("有效年龄: {}\n", .{result});
    }
}

运行

zig run example.zig

输出

年龄 15 错误: OutOfRange
有效年龄: 25
年龄 120 错误: OutOfRange

10. 总结

Zig 的语法简洁而强大,结合静态类型、编译时执行和显式错误处理,提供高性能和安全性。核心特性包括:

  • constvar 声明变量。
  • 丰富的基本类型(i32, u8, ?T, []T)。
  • 控制流(if, for, while, switch)清晰直观。
  • 函数支持错误处理(!T)和编译时优化。
  • 手动内存管理通过分配器实现。

Zig 适合系统编程和性能敏感场景,学习曲线适中,推荐通过 Ziglings(https://ziglings.org/)和官方文档(https://ziglang.org/documentation/)深入学习。

如果你需要更复杂的语法示例(如并发、C 互操作)或有其他问题,请告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注