在枚举中使用 Copy 的超特性时,类型不会实现 Copy 错误

Type does not implement Copy error when using supertrait of Copy in an enum

我是 Rust 特性的新手,所以这可能是由于对超级特性的误解,dyn 或其他任何原因。我正在尝试使用枚举中的特征对象来:

最小示例(无法在 Rust playground 上编译并出现相关错误)是:

#[derive(Copy)]
enum Foo {
    A,
    B(dyn MyTraitWhichIsCopy),
}

trait MyTraitWhichIsCopy: Copy {}

错误是:

error[E0204]: the trait `Copy` may not be implemented for this type
 --> src/lib.rs:1:10
  |
1 | #[derive(Copy)]
  |          ^^^^
...
4 |     B(dyn MyTraitWhichIsCopy),
  |       ---------------------- this field does not implement `Copy`
  |
  = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

For more information about this error, try `rustc --explain E0204`.

调用 rustc --explain E0204 后,我注意到以下内容,这可能是我遇到问题的地方:

The `Copy` trait is implemented by default only on primitive types. If your
type only contains primitive types, you'll be able to implement `Copy` on it.
Otherwise, it won't be possible.

有什么方法可以完成我想做的事情吗?

您可以使用受您的特征约束的通用类型。另请注意,您还需要 Clone 才能拥有 Copy

#[derive(Clone, Copy)]
enum Foo<T: MyTraitWhichIsCopy> {
    A,
    B(T),
}

trait MyTraitWhichIsCopy: Copy {}

Playground

给定的代码中存在几个问题。

  1. 特征 Clone 不是对象安全的,因为方法 clone 引用了原始自身类型,通过 dyn 抽象是未知的。你的trait有一个super traitCopy,而Copy有一个super traitClone,所以你的trait不是object-safe,也就是说不能作为trait对象使用,这也是被称为“dyn 特质”。

  2. Trait 对象是 ?Sized 这意味着它是一个动态大小的类型(DST,其大小在编译时未知)不能用作枚举字段,因为它也使得枚举类型 ?Sized。在大多数情况下,DST 只能通过引用持有,实际上 Box<dyn Trait> 拥有时,否则 &dyn Trait&mut dyn Trait.

  3. Copy 需要超级特征 Clone,这意味着“任何实现 Copy 的类型都必须实现 Clone”。所以当我们希望我们的自定义类型实现Copy时,我们应该首先实现Clone

结论:无法通过trait对象来约束一个类型实现CopyClone,必须用一个Box来持有你的trait对象,并使用手动定义克隆而不是标准克隆特征并为盒装特征对象实现克隆:

trait MyTrait {
    fn clone_dyn(&self) -> Box<dyn MyTrait>;
}

#[derive(Clone)]
struct MyImpl;

impl MyTrait for MyImpl {
    fn clone_dyn(&self) -> Box<dyn MyTrait> {
        Box::new(self.clone())
    }
}

impl Clone for Box<dyn MyTrait> {
    fn clone(&self) -> Self {
        self.clone_dyn()
    }
}

#[derive(Clone)]
enum Foo {
    A,
    B(Box<dyn MyTrait>),
}