Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use PhantomData? #271

Open
panstromek opened this issue Jan 16, 2021 · 2 comments
Open

How to use PhantomData? #271

panstromek opened this issue Jan 16, 2021 · 2 comments

Comments

@panstromek
Copy link

panstromek commented Jan 16, 2021

I am writing this here, because I feel like it belongs to unsafe-code-guidelines, I hope this is the right place to discuss it. It's more of a suggestion to put it in the guidelines than a real question.

Currently I see few problems with this concept.

  1. Discoverability - ie. You don't know you need it unless you already know about it.
    If there's a situation when you should use PhantomData<T> to signal that you own an allocation, compiler doesn't give you any warning, and there's very little information online about it. There's a great chance a lot of users never even heard about it, (at least I think).

  2. It's not exactly clear what it does.
    Docs + Nomicon say that it's needed for variance, which is more or less clear, but then it also says it's needed for drop check, which is not very well explained. Nomicon has a few paragraphs about that, but it's still unclear what exactly goes wrong if you don't specify PhantomData - it just "just leads to unsoundness" but why? I guess I'm just looking for a concrete example here.

  3. It's unclear if you actually need it and how to use it
    For example Docs and Nomicon only talk about use cases with generic types, so it's unclear if you should use it even for things that are not generic.

One specific problem I had is some simple wrapper around opened file from libc:

struct MyFile {
    file: *mut libc::FILE
}

There's a lot of questions about this code.
Is PhantomData<FILE> needed here?
Even when the struct is not generic?
Does it matter when this type is from libc?
libc::FILE doesn't have a destructor, does it matter at all here?
This struct has a Drop impl that calls fclose . If I need PhantomData for drop check, why? Why is Drop impl not enough?
What goes wrong if I don't have PhantomData<FILE> field?

I think clarifying this example specifically would be great, because something like this will probably be the first experience with unsafe code for many users, given that a lot of Rust adopters have to interface with existing C/C++ code.

@thomcc
Copy link
Member

thomcc commented Jan 16, 2021

libc::FILE doesn't impl Drop, so dropck doesn't matter for your example.

That said, the examples here are very bad. I regularly see people confused about how/when to use PhantomData, and the suggestion in the official docs

If your struct does not in fact own the data of type T, it is better to use a reference type, like PhantomData<&'a T> (ideally) or PhantomData<*const T> (if no lifetime applies), so as not to indicate ownership.

is very misleading. There are a number of alternatives here that are more appropriate for common PhantomData use cases before *const T, which will require manual Send/Sync impls.

@RalfJung
Copy link
Member

Related PhantomData issue: rust-lang/nomicon#208.

So given that the variance aspect seems clear, on the technical level the biggest gap seems to be about dropck. And indeed dropck is rather confusing, but the lack of documentation is more of a Reference / Nomicon issue than a UCG issue -- which have already been filed:

The latter contains an example of the kind of unsoundness that can arise when may_dangle is used and PhantomData is missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants