I have a trait where I've implemented PartialEq
(and Eq
) for dyn Trait
. When I use the equality operator on Box<dyn Trait>
, Rc<dyn Trait>
, or Arc<dyn Trait>
it causes the right operand to be moved instead of borrowed. Why? Among other things, this prevents using assert_eq!
directly on such values.
The docs for comparison operators say:
Unlike the arithmetic and logical operators above, these operators implicitly take shared borrows of their operands, evaluating them in place expression context:
a == b;
// is equivalent to
::std::cmp::PartialEq::eq(&a, &b);
This means that the operands don't have to be moved out of.
I can't figure out why the right operand ends up getting moved in this case. Shouldn't the right operand end up in the eq
method as &Box<dyn Trait>
(i.e. a borrow)?
Repro: (Playground)
use std::{
fmt,
rc::Rc,
sync::Arc,
};
trait MyTrait : fmt::Debug {
fn get_val(&self) -> u32;
}
#[derive(Clone, Debug)]
struct MyStruct {
val: u32,
}
impl MyTrait for MyStruct {
fn get_val(&self) -> u32 {
self.val
}
}
impl PartialEq for dyn MyTrait {
fn eq(&self, other: &Self) -> bool {
self.get_val() == other.get_val()
}
}
impl Eq for dyn MyTrait {}
fn main() {
// same error if you change `Box` to `Rc` or `Arc`
let left: Box<dyn MyTrait> = Box::new(MyStruct { val: 0 });
let right: Box<dyn MyTrait> = Box::new(MyStruct { val: 42 });
// assert_eq!(left, right); // error: cannot move out of `*right_val` [...]
let _ = left == right; // moves `right`
let _ = right.get_val(); // error: use of moved value
}
I've found some relatively easy workarounds:
left.eq(&right)
(though doesn't help for assert_eq!
)*left == *right
&left == &right
&*left == &*right
Option
: Some(left) == Some(right)
(limited helpfulness since the values still get moved -- into the Option
s)But (a) it's a little unsatisfying to need them, and (b) I'd still like to understand why this is happening.
This is a compiler bug: issue #31740.
Until the bug is fixed, you'll have to work around it. Either of your options works, though I'd definitely pick one that doesn't move its arguments.