How to copy `PhantomData` of un-`Clone`-able types?

As far as I've understood it PhantomData is just to tell the compiler about the variance of otherwise unused type parameters, and is completely transparent for everything else. In a program I'm working on I had to add to a so far Copy-able type a PhantomData<SomeUncloneableType> and it suddenly broke, even though I believe that it should work (there isn't actually an instance of SomeUncloneableType). Searching the web didn't help.

Because my code is too big to copy it to here, here's a simplification:

use std::marker::PhantomData;

struct Uncloneable;

#[derive(Copy, Clone)]
struct Test<T>
    _marker: PhantomData<T>,

fn a(_: Test<Uncloneable>) {}

fn b(x: Test<Uncloneable>)

fn main() {}

Rustc complains:

error[E0382]: use of moved value: `x`
  --> src/
13 | fn b(x: Test<Uncloneable>)
   |      - move occurs because `x` has type `Test<Uncloneable>`, which does not implement the `Copy` trait
14 | {
15 |     a(x);
   |       - value moved here
16 |     a(x);
   |       ^ value used here after move

For more information about this error, try `rustc --explain E0382`.
error: could not compile `hhdr` due to previous error

This has nothing to do with PhantomData, actually. The problem is that derive(Clone) and derive(Copy) aren't very smart: they automatically add every type parameter to the trait bounds on their respective impls. The solution is to implement Clone and Copy manually, without including T in the trait bounds. (PhantomData<T> is Copy for every choice of T, so that will work.)

impl<T> Clone for Test<T> {
    fn clone(&self) -> Self {

impl<T> Copy for Test<T> {}

It also guides auto-traits like Send and Sync.

It also guides dropck in considering whether interior mutability is present or not.

It also guides dropck in niche "owned" circumstances you'll probably never worry about.

It's not transparent in terms of (non-auto) trait implementations either, e.g. it always implements Default, Copy, Clone, and more.

1 Like