Rust虚类型参数
虚类型参数用来解决这种情况:一个泛型类型的参数从语义上非常重要,但结构体里却不需要存放这种类型的实际数据。换句话说,这种类型 只在类型层面上存在,在运行时不存在。
Rust 为此提供了一个轻量级标记:PhantomData<T>。
它本质是“在类型系统中声明:我与 T 有关联”,但运行时不会实际占用空间。
1. PhantomStruct:与不存在的类型建立语义关系
示例结构体:
pub struct PhantomStruct<A, B> {
first: A,
phantom: PhantomData<B>,
}
这里 B 虽然没有真实字段参与,但你明确告诉编译器:
这个结构体和 B 类型有关,不要把我当作没有这个参数的普通 A 结构体。
例如:
let a: PhantomStruct<i32, char> = PhantomStruct::new(12);
let b: PhantomStruct<i32, f64> = PhantomStruct::new(12);
两个值的内容完全一样,只存了 i32,但 类型绝对不同。所以:
a == b // 完全不允许,因为类型不同
这就是PhantomData最根本的价值:为类型添加“逻辑上的标签”。
2. 虚类型参数在构造函数中的体现
构造器:
impl<A, B> PhantomStruct<A, B> {
pub fn new(first: A) -> PhantomStruct<A, B> {
PhantomStruct {
first,
phantom: PhantomData
}
}
}
调用时B 必须由上下文(如变量类型)提供,否则编译器无法知道 B 是什么。
3. PhantomData用来实现“单位检查”
两个空的枚举表示单位:
enum Yuan {}
enum Dollar {}
然后写一个 Money<Currency> 结构体:
struct Money<Currency> {
amount: f64,
currency: PhantomData<Currency>,
}
通过这里的PhantomData,我们可以告诉编译器:
Money<Yuan> 和 Money<Dollar> 是两种不一样的类型
为Money实现Add方法:
impl<Currency> Add for Money<Currency> {
type Output = Money<Currency>;
fn add(self, rhs: Money<Currency>) -> Money<Currency> {
Money {
amount: self.amount + rhs.amount,
currency: PhantomData
}
}
}
值得注意的是,这里的Add必须要求同一种Currency才能相加。
这意味着:
let chinese: Money<Yuan> = Money { amount: 100.0, currency: PhantomData };
let american: Money<Dollar> = Money { amount: 10.0, currency: PhantomData };
let ok1 = chinese + chinese; // ✓
let ok2 = american + american; // ✓
let error = chinese + american; // ✗ 编译失败!
两个 f64 明明都能加,但由于 PhantomData 定义的“类型标签”,
Rust 从类型层面阻止了逻辑错误。这就是 零运行成本的类型安全,非常 Rust。