Zero-sized type used to mark things that “act like” they own a T.

    PhantomData 是一个mark trait,它主要用在对裸指针操作的地方,比如说

    1. struct Foo<'a, T> {
    2. v: &'a T,
    3. }
    4. fn main() {
    5. let s = String::from("hello");
    6. let f = Foo {v: &s};
    7. drop(s);
    8. drop(f);
    9. }
    1. error[E0505]: cannot move out of `s` because it is borrowed
    2. --> src/phantom.rs:26:10
    3. |
    4. 25 | let f = Foo{v: &s};
    5. | -- borrow of `s` occurs here
    6. 26 | drop(s);
    7. | ^ move out of `s` occurs here
    8. 27 | drop(b);
    9. | - borrow later used here

    比如这个例子中,我们定义了一个struct Foo, 这个结构体封装了T的引用,那么rust在编译时就能检查到,对于Foo实例的引用不能outlive s的引用,换句话说,f 必须活得比 s长

    但是假如Foo 包装了一个对 T的指针?

    1. struct Foo<T> {
    2. pub v: *mut T
    3. }
    4. fn main() {
    5. let mut s = String::from("hello");
    6. let f = Foo {v: &mut s};
    7. drop(s);
    8. drop(f);
    9. }

    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的关系关联起来

    1. struct Foo<'a, T:'a> {
    2. v: *mut T
    3. _mark: PhantomData<&'a T>
    4. }
    5. fn main() {
    6. let mut s = String::from("hello");
    7. let f = Foo {
    8. v: &mut s,
    9. _mark: PhantomData,
    10. };
    11. drop(s);
    12. drop(f);
    13. }

    这里我们需要一个new函数,将s和Foo的lifetime关联起来,这样编译器就知道,s 和 f的lifetime一致,所以s不能在f前被drop掉

    1. struct Foo<'a, T: 'a> {
    2. pub v: *mut T,
    3. _mark: PhantomData<&'a T>
    4. }
    5. impl <'a, T: 'a> Foo<'a, T> {
    6. pub fn new(val: &'a mut T) -> Self {
    7. Self {
    8. v: val,
    9. _mark: PhantomData,
    10. }
    11. }
    12. }
    13. fn main() {
    14. let mut s = String::from("hello");
    15. let f = Foo::new(&mut s);
    16. drop(s);
    17. drop(f);
    18. }
    1. error[E0505]: cannot move out of `s` because it is borrowed
    2. --> src/phantom.rs:21:10
    3. |
    4. 20 | let f = Foo::new(&mut s);
    5. | ------ borrow of `s` occurs here
    6. 21 | drop(s);
    7. | ^ move out of `s` occurs here
    8. 22 | drop(f);
    9. | - borrow later used here
    10. error: aborting due to previous error; 1 warning emitted
    11. For more information about this error, try `rustc --explain E0505`.
    12. error: could not compile `lifetime_demo`