关于 rust:Copy trait 和 PhantomData:这真的应该移动吗?

Copy trait and PhantomData: Should this really move?

PhantomData 以一种令人惊讶的方式与 Copy 交互:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
use std::marker::PhantomData;

#[derive(Copy, Clone)]
pub struct Seconds;

pub struct Meters;

#[derive(Copy, Clone)]
pub struct Val< T > {
    pub v: PhantomData< T >
}

fn main() {
    let v1: Val<Seconds> = Val {v: PhantomData};
    let v2 = v1;
    let v3 = v1;

    let v4: Val<Meters> = Val {v: PhantomData};
    let v5 = v4;
    let v6 = v4;
}

失败如下:

1
2
3
4
5
src/main.rs:20:13: 20:15 error: use of moved value: `v4` [E0382]
src/main.rs:20         let v6 = v4;
                           ^~
src/main.rs:19:13: 19:15 note: `v4` moved here because it has type `Val<Meters>`, which is moved by default
src/main.rs:19         let v5 = v4;

我认为为 Val<Meters> 派生 Copy 会给出 Val<Meters> 复制语义。但显然,只有在 Val 的类型参数 T 也实现了 Copy 的情况下才是正确的。我不明白为什么。

PhantomData 始终实现 Copy,无论其类型参数是否实现。无论如何,如果PhantomData<Meters> 没有实现Copy,我希望编译器会抱怨它无法为Val<Meters> 派生Copy。相反,编译器愉快地为 Val<Meters> 派生了 Copy,但它应用了移动语义。

这种行为是故意的吗?如果是,为什么?


I'd thought that deriving Copy for Val would give Val copy semantics.

但是 Copy 不是为 Val<Meters> 派生的,而是为所有 Val<T> 派生的,其中 T 本身是 Copy.

Github 上有几个未解决的问题,例如这个。我的印象是这不是故意的,而只是 derive 当前工作方式的限制。

您可以通过为 CloneCopy:

手动编写一个全面的实现来解决这个问题

1
2
3
4
5
6
7
impl < T > Clone for Val< T > {
    fn clone(&self) -> Val< T > {
        Val {v: PhantomData}
    }
}

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