简体   繁体   中英

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

I'm new to Rust traits, so this could be due to a misunderstanding of supertraits, dyn or anything else. I'm trying to use a trait object in an enum to:

  • Put a trait bound on the concrete types that can be used in this element of the enum
  • Make sure that the enum can still derive Copy

The minimal example (which fails to compile on Rust playground with the relevant error) is:

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

trait MyTraitWhichIsCopy: Copy {}

The error is:

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`.

After invoking rustc --explain E0204 I noticed the following, which might be where I'm hitting problems:

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.

Is there a way to accomplish what I'm trying to do?

There are several problems in the given code.

  1. The trait Clone is not object-safe because the method clone refers to the raw self type, which is unknown through the dyn abstraction. Your trait has a super trait Copy and Copy has a super trait Clone , therefore your trait is not object-safe, which means it cannot be used as a trait object, which is also known as " dyn trait".

  2. Trait object is ?Sized which means it is a dynamic sized type (DST, whose size is unknown at compile time) that cannot be used as a enum field because it also makes the enum type ?Sized . In most cases, DST can only be held through reference, practically Box<dyn Trait> when owned, otherwise &dyn Trait or &mut dyn Trait .

  3. Copy requires super trait Clone , which means "any type that implements Copy must implement Clone ". So when we wish our custom type to implement Copy , we should implement Clone first.

Conclusion: It is impossible to constraint a type to implement Copy or Clone through a trait object, and you must use a Box to hold your trait object, and use a manually defined clone instead of the standard clone trait and implement clone for the boxed trait object:

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>),
}

You can use a generic type bound by your trait. Also notice that you need Clone to have Copy too:

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

trait MyTraitWhichIsCopy: Copy {}

Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM