在 TiKV 的一次提交里面,同事用了一个 PhantomData
的 marker,当时我就觉得很奇怪,因为从来没用过,这是什么东西,做什么用的?浏览了一下 doc,发现主要是干这几件事情的。
Unused lifetime
在一些 Unsafe 的代码里面,我们很有可能有一个没有用的 lifetime 参数,譬如一个 Slice,我们可能有两个 start 和 end 的 *const T
指针:
struct Slice<'a, T> {
start: *const T,
end: *const T,
}
Slice 的 lifetime 是 'a
,也就是不能超过 'a
的生存周期,但实际 Slice 上面并没有表现出来,因为没有任何地方使用了这个 'a
,为了解决这个问题,我们就可以使用 PhantomData:
use std::marker::PhantomData;
struct Slice<'a, T: 'a> {
start: *const T,
end: *const T,
phantom: PhantomData<&'a T>,
}
上面,我们使用了一个 PhantomData,使用了 lifetime 'a
,这样就明确表示 Slice 的生存周期是 'a
了。
Unused Type
对于一些 generic struct 来说,也有可能自己的 field 并没有使用 Type Parameter,为了解决这个问题,我们也可以使用 PhantomData。在 TiKV 的代码里面,我们就是这么处理的:
pub struct RetryableSendCh<T, C: Sender<T>> {
ch: C,
name: &'static str,
marker: PhantomData<T>,
}
Ownership and Drop check
在 struct 里面加入 PhantomData<T>
也表明我们 own 了 类型 T
的实际数据,这就说是当 struct 被 drop 的时候,一些类型 T
的实例也会被 drop 掉。所以如果我们的 struct 并没有实际的 own 类型 T
的数据,我们需要使用 PhantomData<&'a T>
或者 PhantomData<*const T>
。
譬如我们定义一个 Vec:
struct Vec<T> {
data: *const T, // *const for variance!
len: usize,
cap: usize,
}
在上面的例子中,drop 并不会认为 Vec own 类型 T
的任何数据,也就是说当 drop 这个 vec 的时候,相关的 T
数据并不会被 drop 掉。为了让 drop checker 认为 vec 一定 own 了 T
的数据,我们可以使用:
use std::marker;
struct Vec<T> {
data: *const T, // *const for covariance!
len: usize,
cap: usize,
_marker: marker::PhantomData<T>,
}
小结
Rust 的 PhantomData 对我是一个全新的特性,虽然它已经存在很久了。对于这门语言来说,让我学习的地方还有很多。