Skip to content

Zig 类型转换教程

Zig 有三种类型转换机制:

  • 类型强制转换 (Type Coercion):安全、明确的转换
  • 显式转换 (Explicit Casts):需要明确指定的转换
  • 对等类型解析 (Peer Type Resolution):多操作数时的类型推断

当期望一种类型但提供了另一种类型时:

// 变量声明
const a: u8 = 1;
const b: u16 = a; // u8 → u16 强制转换
// 函数调用
fn foo(x: u16) void {}
foo(a); // u8 → u16 强制转换
// @as 内置函数
const c = @as(u16, a); // 显式使用强制转换

特点:

  • 必须完全明确如何转换
  • 转换必须保证安全
  • 唯一的例外:C 指针

运行时表示相同的值可以增强限定符:

// const 限定:非 const → const
var x: i32 = 10;
const ptr1: *i32 = &x;
const ptr2: *const i32 = ptr1; // ✅ 允许
// volatile 限定:非 volatile → volatile
var y: i32 = 20;
const volatile_ptr: *volatile i32 = &y; // ✅ 允许
// align 限定:大对齐 → 小对齐
const aligned: align(8) i32 = 30;
const less_aligned: align(4) i32 = aligned; // ✅ 允许
// 错误集:子集 → 超集
const SmallError = error{A, B};
const LargeError = error{A, B, C};
const err: SmallError = error.A;
const large_err: LargeError = err; // ✅ 允许
// 指针到 const 可选指针
const arr = [1][*:0]const u8{"hello"};
const slice: []const ?[*:0]const u8 = &arr; // ✅ 允许
// 这些转换在运行时是无操作的
// 因为值的表示没有改变
test "integer widening" {
const a: u8 = 250;
const b: u16 = a; // u8 → u16
const c: u32 = b; // u16 → u32
const d: u64 = c; // u32 → u64
const e: u128 = d; // u64 → u128
try expect(e == 250);
}
test "unsigned to signed widening" {
const a: u8 = 250;
const b: i16 = a; // u8 → i16(能容纳所有值)
try expect(b == 250);
}
test "float widening" {
const a: f16 = 12.34;
const b: f32 = a; // f16 → f32
const c: f64 = b; // f32 → f64
const d: f128 = c; // f64 → f128
try expect(d == 12.34);
}
test "compile-time known narrowing" {
const a: u64 = 255; // 编译期已知为 255
const b: u8 = a; // ✅ 允许:255 能放入 u8
try expect(b == 255);
}
test "compile-time error for narrowing" {
// const c: u64 = 300; // 编译期已知为 300
// const d: u8 = c; // ❌ 编译错误:300 不能放入 u8
}
test "array to slice coercion" {
// *const [N]T → []const T
const str1: []const u8 = "hello";
const str2: []const u8 = &[5]u8{'h','e','l','l','o'};
try expect(std.mem.eql(u8, str1, str2));
// *[N]T → []T(可变版本)
var arr = [5]u8{'h','e','l','l','o'};
const slice: []u8 = &arr;
slice[0] = 'H'; // ✅ 可以修改
}
test "array to many-item pointer" {
var arr = [5]u8{'h','e','l','l','o'};
const ptr: [*]u8 = &arr; // *[5]u8 → [*]u8
try expect(ptr[0] == 'h');
try expect(ptr[4] == 'o');
// ptr[5] 会越界!
}
test "sentinel-terminated conversions" {
// [:x]T → [*:x]T
const slice: [:0]const u8 = "hello";
const ptr: [*:0]const u8 = slice;
try expect(ptr[0] == 'h');
try expect(ptr[5] == 0); // 哨兵
// *T → *[1]T
var x: i32 = 42;
const arr_ptr: *[1]i32 = &x;
try expect(arr_ptr[0] == 42);
}
test "optional coercion" {
// T → ?T
const x: ?i32 = 1234; // i32 → ?i32
const y: ?i32 = null; // null → ?i32
try expect(x.? == 1234);
try expect(y == null);
}
test "nested optional with error union" {
// T → anyerror!?T
const a: anyerror!?i32 = 1234; // i32 → anyerror!?i32
const b: anyerror!?i32 = null; // null → anyerror!?i32
try expect((try a).? == 1234);
try expect((try b) == null);
}
test "error union coercion" {
const Err = error{ Foo, Bar };
// T → Err!T
const success: Err!i32 = 42; // i32 → Err!i32
// error → Err!T
const failure: Err!i32 = error.Foo; // error → Err!i32
try expect((try success) == 42);
try expectError(error.Foo, failure);
}
const E = enum { one, two, three };
const U = union(E) {
one: i32,
two: f32,
three: void, // 可以只有标签
};
test "union and enum coercion" {
// 联合体 → 枚举
const u = U{ .two = 3.14 };
const e: E = u; // U → E
try expect(e == E.two);
// 枚举 → 联合体(当字段只有一个可能值时)
const e2 = E.three;
const u2: U = e2; // E → U
try expect(u2 == E.three);
// 枚举字面量 → 联合体
const u3: U = .three;
try expect(u3 == E.three);
}
const U2 = union(enum) {
a: void, // 只有一个可能值
b: f32, // 有多个可能值
};
test "enum literal to inferred union" {
const u: U2 = .a; // ✅ 允许:.a 可以确定地映射到 U2.a
// const v: U2 = .b; // ❌ 错误:.b 需要初始化 f32 字段
}
// undefined 可以转换为任何类型
const x: i32 = undefined;
const y: []const u8 = undefined;
const z: ?f64 = undefined;
test "tuple to array coercion" {
// 同构元组 → 数组(所有字段类型相同)
const tuple = .{ 5, 6, 7, 8 };
const array: [4]u8 = tuple; // ✅ 允许
// 异构元组不能转换
// const mixed = .{ 5, "hello" };
// const arr2: [2]u8 = mixed; // ❌ 错误
}
函数描述安全性
@bitCast改变类型但保持位表示不安全
@intCast整数类型转换可能截断
@floatCast大浮点数转小浮点数可能精度损失
@truncate整数转换,截断高位安全但丢失数据
@ptrCast指针类型转换不安全
@enumFromInt整数转枚举值运行时检查
@errorFromInt整数转错误代码运行时检查
test "explicit casts" {
// @intCast - 整数转换(运行时检查)
const large: u64 = 300;
const small = @intCast(u8, large); // 可能 panic!
// @truncate - 截断转换(安全但丢失数据)
const x: u16 = 0x1234;
const y: u8 = @truncate(x); // y = 0x34
// @floatCast - 浮点数转换
const a: f64 = 3.1415926535;
const b: f32 = @floatCast(a); // 精度损失
// @bitCast - 位表示转换
const float: f32 = 3.14;
const int: u32 = @bitCast(float); // 重新解释位
// @ptrCast - 指针类型转换
const data: *c_void = @ptrFromInt(0x1000);
const typed: *u32 = @ptrCast(data);
}

11. 对等类型解析 (Peer Type Resolution)

Section titled “11. 对等类型解析 (Peer Type Resolution)”
  • switch 表达式
  • if 表达式
  • while 表达式
  • for 表达式
  • 块中的多个 break 语句
  • 某些二元操作

选择所有对等类型都能强制转换到的类型:

test "peer resolution examples" {
// 整数扩展
const a: i8 = 12;
const b: i16 = 34;
const c = a + b; // 对等类型:i8 和 i16 → i16
try expect(@TypeOf(c) == i16);
// 数组和切片
const str = if (true) "hello" else "world";
// 对等类型:*const [5]u8 和 *const [5]u8 → []const u8
// 可选类型
const opt: ?usize = if (true) 42 else null;
// 对等类型:usize 和 null → ?usize
}
test "complex peer resolution" {
// 混合类型解析
const result = switch (randomBool()) {
true => "yes", // *const [3]u8
false => @as([]const u8, "no"), // []const u8
};
// 对等类型解析为 []const u8
// 错误联合和可选类型
const x: error{Foo}!u32 = 42;
const y = if (x) |val| val + 1 else |err| switch (err) {
error.Foo => null,
};
// 对等类型:u32 和 null → ?u32
}
  1. 优先使用类型强制转换:让编译器处理安全的转换
  2. 谨慎使用显式转换:特别是 @bitCast 和 @ptrCast
  3. 理解对等类型解析:在条件表达式中很有用
  4. 避免模糊转换:如浮点数到整数的自动转换
  5. 利用编译期检查:Zig 会阻止不安全的隐式转换
  6. 明确意图:使用 @as 让转换意图更清晰
fn safeIntCast(comptime T: type, value: anytype) !T {
const Source = @TypeOf(value);
// 检查是否在目标类型范围内
if (@typeInfo(Source) == .Int and @typeInfo(T) == .Int) {
const min = std.math.minInt(T);
const max = std.math.maxInt(T);
if (value < min or value > max) {
return error.OutOfRange;
}
}
return @intCast(T, value);
}
const Config = struct {
port: u16,
timeout: u32,
pub fn init(raw: RawConfig) !Config {
return Config{
.port = try safeIntCast(u16, raw.port),
.timeout = try safeIntCast(u32, raw.timeout),
};
}
};
fn addWithWrap(comptime T: type, a: T, b: T) T {
return switch (@typeInfo(T)) {
.Int => |info| blk: {
const result = @addWithOverflow(a, b);
if (result[1] != 0) {
// 处理溢出
break :blk if (info.signedness == .signed)
std.math.minInt(T)
else
std.math.maxInt(T);
}
break :blk result[0];
},
else => @compileError("Only integer types supported"),
};
}

通过掌握 Zig 的类型转换系统,你可以编写既安全又灵活的代码,充分利用编译器的类型检查能力。