Skip to content

Instantly share code, notes, and snippets.

@tiqwab
Last active March 29, 2020 03:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tiqwab/a879f1315a2880244d0b4151d4588262 to your computer and use it in GitHub Desktop.
Save tiqwab/a879f1315a2880244d0b4151d4588262 to your computer and use it in GitHub Desktop.
One usage of PhantomData in Rust.

According to the doc of PhantomData, one usafe of PhantomData is for unused lifetime parameters like:

struct Slice<'a, T: 'a> {
    start: *mut T,
    end: *mut T,
    _invariant: PhantomData<&'a T>,
}

fn foo<T>(v: &mut Vec<T>) -> Slice<'_, T> {
    let ptr = v.as_mut_ptr();
    unsafe { Slice { start: ptr, end: ptr.add(v.len()), _invariant: PhantomData } }
}

pub fn main() {
    let mut v = vec!(1, 2, 3);
    let slice = foo(&mut v);

    drop(v);

    let s1 = unsafe { &mut *slice.start };
    let s2 = slice.start;

    *s1 += 1;
    println!("{}", s1);

    unsafe { *s2 += 1 };
    println!("{}", unsafe { *s2 });
}

It causes an compile error:

error[E0505]: cannot move out of `v` because it is borrowed
  --> phantom.rs:35:10
   |
33 |     let slice = foo(&mut v);
   |                     ------ borrow of `v` occurs here
34 | 
35 |     drop(v);
   |          ^ move out of `v` occurs here
...
38 |     let s1 = unsafe { &mut *slice.start };
   |                       ----------------- borrow later used here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0505`.

If we don't use PhantomData, it generates no compile error.

struct Slice<T> {
    start: *mut T,
    end: *mut T,
}

fn foo<T>(v: &mut Vec<T>) -> Slice<T> {
    let ptr = v.as_mut_ptr();
    unsafe { Slice { start: ptr, end: ptr.add(v.len()) } }
}

pub fn main() {
    let mut v = vec!(1, 2, 3);
    let slice = foo(&mut v);

    drop(v);
    // drop(slice);

    let s1 = unsafe { &mut *slice.start };
    let s2 = slice.start;

    *s1 += 1;
    println!("{}", s1);

    unsafe { *s2 += 1 };
    println!("{}", unsafe { *s2 });
}

Borrow in foo is important here. Without it, no compile error occurs.

use std::marker::PhantomData;

struct Slice<'a, T: 'a> {
    start: *mut T,
    end: *mut T,
    _invariant: PhantomData<&'a T>,
}

pub fn main() {
    let mut v = vec!(1, 2, 3);
    let ptr = v.as_mut_ptr(); // pointer can be copied, so borrow doesn't occur.
    let slice = unsafe { Slice { start: ptr, end: ptr.add(v.len()), _invariant: PhantomData } };

    drop(v);
    // drop(slice);

    let s1 = unsafe { &mut *slice.start };
    let s2 = slice.start;

    *s1 += 1;
    println!("{}", s1);

    unsafe { *s2 += 1 };
    println!("{}", unsafe { *s2 });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment