联合体 (Unions)
联合体(Union)是一种特殊的数据结构,它允许其多个字段共享同一块内存区域。这意味着在任何时候,联合体实例只能有效地持有其一个字段的值。联合体主要用于与 C 语言代码交互 (FFI),或者在需要极度节省内存且能安全管理活跃字段的底层编程场景。
警告: 使用联合体需要开发者自行承担跟踪哪个字段当前是活跃(有效)的责任。错误地访问非活跃字段可能导致未定义行为或内存损坏。因此,除非有充分理由并能确保安全,否则应优先使用枚举(Enum)来表示“多种可能之一”的数据。
定义联合体
使用 union
关键字定义联合体,并在花括号 {}
内声明其所有可能的字段。
-- 一个可能持有整数或浮点数的联合体
union IntOrFloat {
i: i32,
f: f32,
}
-- 用于模拟 C 语言联合体进行 FFI
^repr(.c) -- (假设) 指定 C 兼容布局
union CEventArgs {
key_event: KeyEvent, -- 假设 KeyEvent 是一个 struct
mouse_event: MouseEvent, -- 假设 MouseEvent 是一个 struct
}
实例化和访问联合体
联合体的实例化通常需要明确指定要初始化的那个字段。访问字段也使用点 (.
) 操作符,但必须确保访问的是当前活跃的字段。
test {
-- 初始化为整数
let value = IntOrFloat { .i 10 }
-- 安全地访问整数(因为我们知道它是活跃的)
println("Integer value: {}", value.i);
-- **危险操作**:写入浮点字段会覆盖整数数据
value.f = 3.14;
-- **危险操作**:此时再读取 i 字段会得到未定义或损坏的数据
-- println("Integer value after float write: {}", value.i); -- <-- 未定义行为!
-- 安全地访问浮点数
println("Float value: {}", value.f);
}
安全使用联合体:带标签的联合体 (Tagged Unions)
为了安全地使用联合体,常见的模式是将其与一个枚举(作为标签)组合在一个结构体中,用标签来明确指示当前哪个联合体字段是活跃的。Flurry 的枚举 (Enum) 本身就是一种更安全、更推荐的带标签联合体的实现方式。
-- 使用枚举代替裸联合体是更安全的方式
enum SafeIntOrFloat {
integer(i32),
float(f32),
}
test {
let value = SafeIntOrFloat.integer(10);
match value {
.integer(i) => println("Integer: {}", i),
.float(f) => println("Float: {}", f),
}
}
联合体作为命名空间与关联函数
与 struct
和 enum
一样,union
定义也是一个命名空间,可以在其内部定义关联函数。但这通常不太常见,因为操作联合体实例需要外部信息(哪个字段是活跃的)。
总结
联合体提供了底层内存布局的控制能力,允许多个字段共享内存,主要用于 FFI 和特殊的内存优化场景。然而,由于其固有的不安全性(需要手动跟踪活跃字段),强烈建议优先使用枚举 (Enum) 来表示互斥的数据变体。如果必须使用联合体,务必采取额外的机制(如外部标签)来确保访问安全。