Hi all,
I'm trying to make a generic equivalent of PartialEq
for traits (Arc<dyn Foo>
, specifically), and it almost works. Except that my toy example fails to compile because comparisons somehow seem to cause a move:
fn test(b: Baz) -> bool {
b.op == b.op
}
fails with
error[E0505]: cannot move out of `b.op` because it is borrowed
|
43 | fn test(b: Baz) -> bool {
| - binding `b` declared here
44 | b.op == b.op
| ---- ^^^^ move out of `b.op` occurs here
| |
| borrow of `b.op` occurs here
|
help: clone the value to increment its reference count
|
44 | b.op.clone() == b.op
| ++++++++
Changing it to
&b.op == &b.op
or
b.op.deref() == b.op.deref()
compiles fine... but that's not what #[derive(PartialEq)]
does (my ultimate goal).
I assume the problem would be somewhere in deref coercion or other implicit trait use, but I can't guess what the culprit might be and there doesn't seem to be a way to make the compiler say what led it to that point.
What am I doing wrong?
kpreid
February 1, 2025, 5:41am
2
I think this is a compiler bug; this appears to be a report with the same key code structure:
opened 06:04PM - 01 Jul 24 UTC
A-diagnostics
A-borrow-checker
T-compiler
A-docs
C-bug
A-trait-objects
[The following code produces a move error](https://play.rust-lang.org/?version=n… ightly&mode=debug&edition=2021&gist=45b377544d27adffcaa1f0b38cbe22e0):
```rust
trait Animal {
fn noise(&self) -> String;
}
impl PartialEq for dyn Animal {
fn eq(&self, other: &Self) -> bool {
self.noise() == other.noise()
}
}
fn f(a1: &Box<dyn Animal>, a2: &Box<dyn Animal>) {
println!("{}", *a1 == *a2); // doesn't work
}
```
```
error[E0507]: cannot move out of `*a2` which is behind a shared reference
--> src/lib.rs:12:27
|
12 | println!("{}", *a1 == *a2); // doesn't work
| ^^^ move occurs because `*a2` has type `Box<dyn Animal>`, which does not implement the `Copy` trait
````
[But according to the reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#comparison-operators), it should be equivalent to the following, which does work:
```rust
fn f(a1: &Box<dyn Animal>, a2: &Box<dyn Animal>) {
println!("{}", PartialEq::eq(&*a1, &*a2)); // works
}
```
It seems to be specific to trait objects; e.g. replacing `Box<dyn Animal>` with `Box<[String]>` will make both versions of `f` compile.
_Originally reported in https://github.com/rust-lang/rust/issues/123056#issuecomment-2067785879_
It is a longstanding bug.
opened 12:24AM - 18 Feb 16 UTC
A-type-system
A-borrow-checker
T-compiler
A-docs
C-bug
T-types
A-trait-objects
The following code has a compilation error, but it should be a valid program [(p… layground)](https://play.rust-lang.org/?gist=f39d36c50f0f54b8c3fd&version=nightly)
([Updated code in 2021 edition](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=088ae790d8e2c2ce283918a344c8cf31))
``` rust
trait Trait { }
impl<T> Trait for T { }
impl<'a> PartialEq for Trait + 'a {
fn eq(&self, other: &Self) -> bool { false }
}
fn main() {
let x = Box::new(1) as Box<Trait>;
let equal = x == x;
}
```
```
<anon>:10:22: 10:23 error: cannot move out of `x` because it is borrowed [E0505]
<anon>:10 let equal = x == x;
^
<anon>:10:17: 10:18 note: borrow of `x` occurs here
<anon>:10 let equal = x == x;
^
```
- This only occurs for the reflexive (x == x) case.
- It doesn't actually compile into a move in `x == y`.
- Fat boxes like `Box<[T]>` don't behave like this
The error is the same when using this equality impl instead:
``` rust
impl<'a> PartialEq for Box<Trait + 'a> {
fn eq(&self, other: &Self) -> bool { false }
}
```
There's an ergonomic workaround for Box<dyn Trait>
, but I'm not sure about Arc<dyn Trait>
. I'll play around and post again if I find one.
(As in, not requiring changes at the call site. *b.op == *b.op
works.)
1 Like
Add this impl:
impl PartialEq<dyn Foo> for Arc<dyn Foo> {
fn eq(&self, other: &dyn Foo) -> bool {
(**self).dyn_eq((*other).any_ref())
}
}
Thanks! It didn't occur to me that this might be a compiler bug, glad I asked.