An easy way to create a type wrapper?

I have the Uuid type for object identification in my database. I want to create a wrapper for the identifier that will store the type of the referenced object. For example, for storing User I would indicate the type Id<User> and then I could write methods like:

fn load_user(self, id: Id<User>) -> Option<User>

Moreover, the User itself would be defined as follows:

pub struct User {
    id: Id<Self>,
    name: String,
    // ..
} 

But some problems arose during the implementation:

  1. Lots of boilerplate code, because of PhantomData I can't just use deriving
pub struct Id<T>(Uuid, PhantomData<*const T>);

impl<T> Clone for Id<T> { /* .. */ }
impl<T> Copy for Id<T> {}
impl<T> PartialOrd // ..
impl<T> Ord // ..
impl<T> PartialEq // ..
impl<T> Eq // ..
impl<T> Hash // ..
impl<T> Deref // ..
impl<T> DerefMut // ..
impl<T> Debug // ..
impl<T> // ..

I wish I had some way to avoid this. Are there some ways to make it shorter?

  1. I see no way to implement Send and Sync for my wrapper without using unsafe. The PhantomData<*const T> doesn't implement these traits. My project is quite high-level and I want to write it only safe.

Could these problems be solved by using PhantomData<T> instead of PhantomData<*const T>? Is there a reason you're using a phantom raw pointer in the first place?

The reason is documentation says:
Adding a field of type PhantomData indicates that your type owns data of type T.
But in my case it is not so.
A PhantomData<&T> needs to indicate lifetime. Perhaps it would be possible to use 'static lifetime, but that would require a constraint on the type to be static bound.

I guess you chose *const T because you didn't want the Drop implications. But as a result, you become !Send and !Sync, and you would rather have the compiler reason about those for you as per normal.

As per the table of PhantomData patterns, try using PhantomData<fn() -> T> instead.

5 Likes

I'd say that sentence in the documentation is a guideline, not a hard-and-fast rule. You should choose the PhantomData type that gives you the auto traits and borrow check/drop check behavior that's appropriate in your case.

Thanks, this is what I need

FWIW, PhantomData<T> here is just as usable as PhantomData<fn() -> T>, since there is no impl<#[may_dangle]…> Drop (there isn't even a Drop impl to begin with!)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.