Rust PhantomData

Rust中的幽灵类型(PhantomData)

现在有两个数据结构分别为User和Product,它们都有一个u64类型的id,有一个需求是每个数据结构的id只能和同种类型的id比较,如果user.id和product.id比较,编译器就能直接报错,该如何实现?

首先,可以设计一个inner数据结构来表示id,利用泛型将外层数据结构Self传入,已达到两个不同类型的inner不能比较的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

#[derive(Debug, Default, PartialEq, Eq)]
struct Identifier<T> {
inner: u64,
}

#[derive(Debug, Default, PartialEq, Eq)]
struct User {
id: Identifier<Self>,
}

#[derive(Debug, Default, PartialEq, Eq)]
struct Product {
id: Identifier<Self>,
}

fn main() {
let user = User::default();
let product = Product::default();

assert_ne!(user.id, product.id);
}

但此时上述代码无法通过编译,报错信息为

parameter T is never used

泛型T未被使用,编译器认为T是多余的

此时我们可以通过PhantomData来解决,PhantomData顾名思义就是幽灵类型,它被广泛用在处理,数据结构定义过程中不需要,但是在实现过程中需要的泛型参数。PhantomData实际上长度为零,是个ZST(Zero-Sized Type),就像不存在一样,唯一作用就是类型的标记。

最终效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::marker::PhantomData;

#[derive(Debug, Default, PartialEq, Eq)]
struct Identifier<T> {
inner: u64,
_tag: PhantomData<T>,
}

#[derive(Debug, Default, PartialEq, Eq)]
struct User {
id: Identifier<Self>,
}

#[derive(Debug, Default, PartialEq, Eq)]
struct Product {
id: Identifier<Self>,
}

fn main() {
let user = User::default();
let product = Product::default();

assert_ne!(user.id, product.id); //mismatched types expected struct `Identifier<User>` found struct `Identifier<Product>`
}

参考

[1]陈天·Rust编程第一课