Skip to content
zig 版本:0.14.0

联合类型

联合类型(union)是一种特殊的类型,它在内存中划分出一块空间,用于存储多种不同类型的值,但在任何给定时间点,只能存储其中一种类型的值。

基本使用

联合类型的基本使用示例如下:

zig
const Payload = union {
    int: i64,
    float: f64,
    boolean: bool,
};

pub fn main() !void {
    var payload = Payload{ .int = 1234 };
    payload = Payload{ .int = 9 };
    // var payload_1: Payload = .{ .int = 1234 };

    print("{}\n", .{payload.int});
}
zig
const print = @import("std").debug.print;

const Payload = union {
    int: i64,
    float: f64,
    boolean: bool,
};

pub fn main() !void {
    var payload = Payload{ .int = 1234 };
    payload = Payload{ .int = 9 };
    // var payload_1: Payload = .{ .int = 1234 };

    print("{}\n", .{payload.int});
}

🅿️ 提示

需要注意的是,Zig 不保证普通联合类型在内存中的具体表现形式。如果需要确保其内存布局与 C 兼容,可以使用 extern unionpacked union

如果要初始化一个在编译期已知字段名的联合类型,可以使用 @unionInit 内建函数:

zig
@unionInit(
    comptime Union: type,
    comptime active_field_name: []const u8,
    init_expr
) Union
zig
const Payload = union {
    int: i64,
    float: f64,
    boolean: bool,
};
// 通过 @unionInit 初始化一个联合类型
const payload = @unionInit(Payload, "int", 666);

标记联合(Tagged Union)

联合类型在定义时可以使用枚举进行标记,并且可以通过 @as 函数将联合类型直接视为声明的枚举来使用或比较。

换句话说,普通的 union 可以存储多种值,但无法跟踪当前存储的是哪种类型。而标记联合tag union)则在 union 的基础上增加了类型跟踪能力,使其更加安全和易用。

🅿️ 提示

简单来说,标记联合可以明确辨别当前存储的类型,使用起来更方便。

而普通联合类型在 ReleaseSmallReleaseFast 构建模式下,将无法检测出错误的读取行为。例如,将一个 u64 存储在一个 union 中,然后尝试将其读取为一个 f64,这在程序员看来是非法的,但在这些构建模式下运行时却可能不会报错!

zig
// 一个枚举,用于给联合类型挂上标记
const ComplexTypeTag = enum {
    ok,
    not_ok,
};

// 带标记的联合类型
const ComplexType = union(ComplexTypeTag) {
    ok: u8,
    not_ok: void,
};

const c = ComplexType{ .ok = 42 };
// 可以直接将标记联合类型作为枚举来使用,这是合法的
try expect(@as(ComplexTypeTag, c) == ComplexTypeTag.ok);

// 使用 switch 进行匹配
switch (c) {
    ComplexTypeTag.ok => |value| try expect(value == 42),
    ComplexTypeTag.not_ok => unreachable,
}

// 使用 zig 的 meta 库获取对应的 tag
try expect(std.meta.Tag(ComplexType) == ComplexTypeTag);
zig
const std = @import("std");
const expect = std.testing.expect;

pub fn main() !void {
    // 一个枚举,用于给联合类型挂上标记
    const ComplexTypeTag = enum {
        ok,
        not_ok,
    };

    // 带标记的联合类型
    const ComplexType = union(ComplexTypeTag) {
        ok: u8,
        not_ok: void,
    };

    const c = ComplexType{ .ok = 42 };
    // 可以直接将标记联合类型作为枚举来使用,这是合法的
    try expect(@as(ComplexTypeTag, c) == ComplexTypeTag.ok);

    // 使用 switch 进行匹配
    switch (c) {
        ComplexTypeTag.ok => |value| try expect(value == 42),
        ComplexTypeTag.not_ok => unreachable,
    }

    // 使用 zig 的 meta 库获取对应的 tag
    try expect(std.meta.Tag(ComplexType) == ComplexTypeTag);
}

如果要修改实际的载荷(即标记联合中的值),可以使用 * 语法捕获指针类型:

zig
// 枚举,用于给联合类型打上标记
const ComplexTypeTag = enum {
    ok,
    not_ok,
};

// 带标记的联合类型
const ComplexType = union(ComplexTypeTag) {
    ok: u8,
    not_ok: void,
};

var c = ComplexType{ .ok = 42 };

// 使用 switch 进行匹配
switch (c) {
    // 捕获了标记联合值的指针,用于修改值
    ComplexTypeTag.ok => |*value| value.* += 1,
    ComplexTypeTag.not_ok => unreachable,
}

try expect(c.ok == 43);
zig
const std = @import("std");
const expect = std.testing.expect;

pub fn main() !void {
    // 枚举,用于给联合类型打上标记
    const ComplexTypeTag = enum {
        ok,
        not_ok,
    };

    // 带标记的联合类型
    const ComplexType = union(ComplexTypeTag) {
        ok: u8,
        not_ok: void,
    };

    var c = ComplexType{ .ok = 42 };

    // 使用 switch 进行匹配
    switch (c) {
        // 捕获了标记联合值的指针,用于修改值
        ComplexTypeTag.ok => |*value| value.* += 1,
        ComplexTypeTag.not_ok => unreachable,
    }

    try expect(c.ok == 43);
}

还支持使用 @tagName 来获取当前活跃字段的名称(返回一个编译期常量 [:0]const u8,即字符串):

zig
const Small2 = union(enum) {
    a: i32,
    b: bool,
    c: u8,
};

const name = @tagName(Small2.a);
// 这个返回值将会是 a

🅿️ 提示

上面的 Small2 也是一个标记联合类型,不过它的标记是一个匿名的枚举类型,并且该枚举类型成员为:a, b, c

自动推断

Zig 也支持自动推断联合类型:

zig
const Number = union {
    int: i32,
    float: f64,
};

// 自动推断
const i: Number = .{ .int = 42 };

extern union

extern union 保证其内存布局与目标 C ABI 兼容。

具体用法请参见 extern struct 部分。

packed union

packed union 保证其内存布局与声明顺序相同,并且尽可能紧凑。具体用法请参见 packed struct 部分。