Introduce Arc<dyn Trait> in Enum which impl PartialEq

I got the following error and struggle to stop compiler complaining

Error

cannot move out of *__arg1_0 which is behind a shared reference
move occurs because *__arg1_0 has type Arc<dyn MyTrait>, which does not implement the Copy trait

pub trait MyTrait {
    fn compute(&self) -> &[i32]; 
}

impl PartialEq for dyn MyTrait {
    fn eq(&self, other: &Self) -> bool {
        self.compute().eq(other.compute())
    }
}

#[derive(PartialEq)]
pub enum MyEnum {
    MyTrait(Arc<dyn MyTrait>),
}

What I am trying to do is that I have a pub enum with derive macro PartialEq and I want to introduce new enum value with trait, Arc<dyn MyTrait> and sadly I got error, the above code is the short version of code that also come out error.

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum MyEnum {
   NewEnum(Arc<dyn MyTrait>)
}
1 Like

The error is actually quite interesting (and might hint on some bug in typechecker). If we expand the #[derive(PartialEq)], we get this:

impl ::core::cmp::PartialEq for MyEnum {
    #[inline]
    fn eq(&self, other: &MyEnum) -> bool {
        match (self, other) {
            (MyEnum::MyTrait(__self_0), MyEnum::MyTrait(__arg1_0)) =>
                *__self_0 == *__arg1_0
        }
    }
}

Which, if pasted directly instead of deriving, gives us the same error. However, if we desugar the equivalence check, i.e. do it the following way:

impl ::core::cmp::PartialEq for MyEnum {
    #[inline]
    fn eq(&self, other: &MyEnum) -> bool {
        match (self, other) {
            (MyEnum::MyTrait(__self_0), MyEnum::MyTrait(__arg1_0)) =>
                <_ as PartialEq>::eq(&*__self_0, &*__arg1_0)
        }
    }
}

...the error suddenly disappears.

I wasn't able to minimize this further yet - with the plain free function the error disappears too.

upd: it seems to be something specific to dyn Trait. Replacing it with String or str get rid of an error, too.

3 Likes

I've tried then to get rid of an Arc. With Box, the error is essentially the same, but with plain references it gets even weirder:

pub enum MyEnum<'a> {
    MyTrait(&'a dyn MyTrait),
}

impl<'a> ::core::cmp::PartialEq for MyEnum<'a> {
    #[inline]
    fn eq(&self, other: &MyEnum<'a>) -> bool {
        match (self, other) {
            (MyEnum::MyTrait(__self_0), MyEnum::MyTrait(__arg1_0)) =>
                *__self_0 == *__arg1_0,
        }
    }
}

Error:

error: lifetime may not live long enough
  --> src/main.rs:21:30
   |
17 | impl<'a> ::core::cmp::PartialEq for MyEnum<'a> {
   |      -- lifetime `'a` defined here
...
21 |             (MyEnum::MyTrait(__self_0), MyEnum::MyTrait(__arg1_0)) =>
   |                              ^^^^^^^^ assignment requires that `'a` must outlive `'static`

Just where is that assignment they are talking about?!

2 Likes

I have no idea either :upside_down_face:

Filed an issue, probably someone knowing the compiler internals could clarify this.

2 Likes

That playground works with impl PartialEq for &dyn MyTrait {...}, just added the &.

I'm not sure where the assignment is, but the 'static requirement is coming from the implicit lifetime in dyn MyTrait. This change fixes the problem:

-impl PartialEq for dyn MyTrait {
+impl PartialEq for dyn MyTrait + '_ {

Edit: At least, it does for references. The Box version is still broken:

error[E0507]: cannot move out of `*__arg1_0` which is behind a shared reference
  --> src/main.rs:22:30
   |
22 |                 *__self_0 == *__arg1_0,
   |                              ^^^^^^^^^ move occurs because `*__arg1_0` has type `Box<dyn MyTrait>`, which does not implement the `Copy` trait

(and similarly for Arc)

1 Like
3 Likes