























Rust 中的 union(联合体)在内存布局和底层逻辑上与 C 语言的 union 完全一致,其存在的首要目的就是为了与 C 语言进行无缝的 FFI(外部函数接口) 交互。
但在类型安全、生命周期管理以及编译器约束方面,两者存在着本质的区别:
union 的任何字段都是完全自由且隐式安全的。编译器允许你随时读取任意字段(即使你刚刚写入的是另一个字段),这在 C 中极易引发未定义行为(UB)。unsafe 块中。因为 Rust 编译器在编译期无法追踪当前 union 内存中到底存放的是哪一个字段的有效数据。#[repr(C)] // 保持与 C 一致的内存布局
union MyUnion {
f1: u32,
f2: f32,
}
let mut u = MyUnion { f1: 1 };
u.f2 = 2.0; // 写入是安全的
// 读取必须使用 unsafe
unsafe {
println!("{}", u.f2);
}
union。union 字段的类型有极强的限制:
union 的字段不能包含实现了 Drop 特征的类型(例如 String、Vec<T>、Box<T> )。union 离开作用域被销毁时,编译器不知道应该调用哪个字段的 Drop 函数。如果盲目调用,会导致内存双重释放(Double Free)或释放了错误的指针。Copy 类型,必须使用 std::mem::ManuallyDrop 包裹字段,显式告诉编译器放弃自动析构,由开发者手动管理生命周期。switch-case 配合一个外部的 tag(标签)变量手动判断。unsafe 块中对 union 进行模式匹配(Pattern Matching),但由于编译器无法保证活跃字段,匹配必须显式指定你想要读取的那个具体字段名称。// 必须在 unsafe 块中对指定字段进行匹配
unsafe {
match u {
MyUnion { f1: 10 } => println!("f1 is 10"),
MyUnion { f1 } => println!("f1 is {}", f1),
}
}
union 可以使用部分标准派生宏(如 #[derive(Clone, Copy)]),但不能派生 PartialEq、Eq、Hash 或 Debug。因为这些特性的实现依赖于清晰地读取字段值,而编译器在安全上下文中无法得知哪一个字段有效。enum在 Rust 中,如果你不需要和 C 语言的代码做内存对接,绝对不应该使用 union。
Rust 官方推荐的惯用替代方案是 enum(标签联合/代数数据类型)。Rust 的 enum 在底层是一个“安全的联合体”,它不仅包裹了数据,还在内存中额外占用 1 个字节作为“标签(Tag)”,从而让编译器在编译期和运行时都能完美追踪当前活跃的究竟是哪一个分支:
// Rust 官方推荐的“安全联合体”写法
enum MySafeUnion {
F1(u32),
F2(String), // 完美支持 Drop 类型
} // 它是安全的,支持模式匹配,读取不需要 unsafe
参考资料:
1.C++联合
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。