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:
I wish I had some way to avoid this. Are there some ways to make it shorter?
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.
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.
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!)