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 here
26 | drop(s);
| ^ move out of `s` occurs here
27 | 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 here
21 | drop(s);
| ^ move out of `s` occurs here
22 | drop(f);
| - borrow later used here
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0505`.
error: could not compile `lifetime_demo`