Zig 类型转换教程
1. 类型转换概览
Section titled “1. 类型转换概览”Zig 有三种类型转换机制:
- 类型强制转换 (Type Coercion):安全、明确的转换
- 显式转换 (Explicit Casts):需要明确指定的转换
- 对等类型解析 (Peer Type Resolution):多操作数时的类型推断
2. 类型强制转换 (Type Coercion)
Section titled “2. 类型强制转换 (Type Coercion)”2.1 何时发生
Section titled “2.1 何时发生”当期望一种类型但提供了另一种类型时:
// 变量声明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 指针
3. 严格限定符转换
Section titled “3. 严格限定符转换”3.1 限定符增强
Section titled “3.1 限定符增强”运行时表示相同的值可以增强限定符:
// const 限定:非 const → constvar x: i32 = 10;const ptr1: *i32 = &x;const ptr2: *const i32 = ptr1; // ✅ 允许
// volatile 限定:非 volatile → volatilevar 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; // ✅ 允许3.2 指针转换
Section titled “3.2 指针转换”// 指针到 const 可选指针const arr = [1][*:0]const u8{"hello"};const slice: []const ?[*:0]const u8 = &arr; // ✅ 允许
// 这些转换在运行时是无操作的// 因为值的表示没有改变4. 数值类型扩展
Section titled “4. 数值类型扩展”4.1 整数扩展
Section titled “4.1 整数扩展”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);}4.2 浮点数扩展
Section titled “4.2 浮点数扩展”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);}4.3 编译期已知值的缩小
Section titled “4.3 编译期已知值的缩小”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}5. 切片、数组和指针转换
Section titled “5. 切片、数组和指针转换”5.1 数组到切片
Section titled “5.1 数组到切片”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'; // ✅ 可以修改}5.2 数组到多项目指针
Section titled “5.2 数组到多项目指针”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] 会越界!}5.3 哨兵终止的切片和指针
Section titled “5.3 哨兵终止的切片和指针”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);}6. 可选类型转换
Section titled “6. 可选类型转换”6.1 基本转换
Section titled “6.1 基本转换”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);}6.2 嵌套转换
Section titled “6.2 嵌套转换”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);}7. 错误联合类型转换
Section titled “7. 错误联合类型转换”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);}8. 联合体和枚举转换
Section titled “8. 联合体和枚举转换”8.1 带标签的联合体 ↔ 枚举
Section titled “8.1 带标签的联合体 ↔ 枚举”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);}8.2 推断枚举标签类型
Section titled “8.2 推断枚举标签类型”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 字段}9. 其他特殊转换
Section titled “9. 其他特殊转换”9.1 undefined 转换
Section titled “9.1 undefined 转换”// undefined 可以转换为任何类型const x: i32 = undefined;const y: []const u8 = undefined;const z: ?f64 = undefined;9.2 元组到数组转换
Section titled “9.2 元组到数组转换”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; // ❌ 错误}10. 显式转换 (Explicit Casts)
Section titled “10. 显式转换 (Explicit Casts)”10.1 内置显式转换函数
Section titled “10.1 内置显式转换函数”| 函数 | 描述 | 安全性 |
|---|---|---|
@bitCast | 改变类型但保持位表示 | 不安全 |
@intCast | 整数类型转换 | 可能截断 |
@floatCast | 大浮点数转小浮点数 | 可能精度损失 |
@truncate | 整数转换,截断高位 | 安全但丢失数据 |
@ptrCast | 指针类型转换 | 不安全 |
@enumFromInt | 整数转枚举值 | 运行时检查 |
@errorFromInt | 整数转错误代码 | 运行时检查 |
10.2 实用示例
Section titled “10.2 实用示例”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)”11.1 发生场景
Section titled “11.1 发生场景”switch表达式if表达式while表达式for表达式- 块中的多个
break语句 - 某些二元操作
11.2 工作原理
Section titled “11.2 工作原理”选择所有对等类型都能强制转换到的类型:
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}11.3 复杂示例
Section titled “11.3 复杂示例”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}12. 最佳实践总结
Section titled “12. 最佳实践总结”- 优先使用类型强制转换:让编译器处理安全的转换
- 谨慎使用显式转换:特别是
@bitCast和@ptrCast - 理解对等类型解析:在条件表达式中很有用
- 避免模糊转换:如浮点数到整数的自动转换
- 利用编译期检查:Zig 会阻止不安全的隐式转换
- 明确意图:使用
@as让转换意图更清晰
13. 实用模式
Section titled “13. 实用模式”13.1 安全数值转换函数
Section titled “13.1 安全数值转换函数”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);}13.2 类型安全的 API
Section titled “13.2 类型安全的 API”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), }; }};13.3 泛型数值处理
Section titled “13.3 泛型数值处理”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 的类型转换系统,你可以编写既安全又灵活的代码,充分利用编译器的类型检查能力。