Zero-sized type used to mark things that “act like” they own a T.
PhantomData 是一个mark trait,它主要用在对裸指针操作的地方,比如说
struct Foo<'a, T> {v: &'a T,}fn main() {let s = String::from("hello");let f = Foo {v: &s};drop(s);drop(f);}
error[E0505]: cannot move out of `s` because it is borrowed--> src/phantom.rs:26:10|25 | let f = Foo{v: &s};| -- borrow of `s` occurs here26 | drop(s);| ^ move out of `s` occurs here27 | drop(b);| - borrow later used here
比如这个例子中,我们定义了一个struct Foo, 这个结构体封装了T的引用,那么rust在编译时就能检查到,对于Foo实例的引用不能outlive s的引用,换句话说,f 必须活得比 s长
但是假如Foo 包装了一个对 T的指针?
struct Foo<T> {pub v: *mut T}fn main() {let mut s = String::from("hello");let f = Foo {v: &mut s};drop(s);drop(f);}
rust 不会对裸指针做lifetime check,这要求开发者自己保证,在要对裸指针进行操作的时候,比如derefernece,需要用到unsafe,这时候需要开发者自己保证内存安全。但假如我希望以上这段代码也能有编译时检查,需要怎么做?
这时候可以让Foo引入 PhantomData, 它的目的就是让编译器知道,他引用了T,存在一个generic lifetime ‘a, 但是发现现在这样并不能解决问题,在so上提了个问题 https://stackoverflow.com/questions/67759595/phantomdata-use-case 不久就有大神回复了
因为编译器只知道对于Foo,所有对T的引用不能超过’a, 但是字段v是裸指针,和T并没有引用关系,所以需要一个办法将 s 和 f的关系关联起来
struct Foo<'a, T:'a> {v: *mut T_mark: PhantomData<&'a T>}fn main() {let mut s = String::from("hello");let f = Foo {v: &mut s,_mark: PhantomData,};drop(s);drop(f);}
这里我们需要一个new函数,将s和Foo的lifetime关联起来,这样编译器就知道,s 和 f的lifetime一致,所以s不能在f前被drop掉
struct Foo<'a, T: 'a> {pub v: *mut T,_mark: PhantomData<&'a T>}impl <'a, T: 'a> Foo<'a, T> {pub fn new(val: &'a mut T) -> Self {Self {v: val,_mark: PhantomData,}}}fn main() {let mut s = String::from("hello");let f = Foo::new(&mut s);drop(s);drop(f);}
error[E0505]: cannot move out of `s` because it is borrowed--> src/phantom.rs:21:10|20 | let f = Foo::new(&mut s);| ------ borrow of `s` occurs here21 | drop(s);| ^ move out of `s` occurs here22 | drop(f);| - borrow later used hereerror: aborting due to previous error; 1 warning emittedFor more information about this error, try `rustc --explain E0505`.error: could not compile `lifetime_demo`
