Kyle_S-C
Kyle_S-C

Reputation: 1177

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:

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?

Upvotes: 1

Views: 1768

Answers (2)

Alsein
Alsein

Reputation: 4775

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

Upvotes: 5

Netwave
Netwave

Reputation: 42708

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

Upvotes: 1

Related Questions