У меня есть черта, в которой я реализовал PartialEq
(и Eq
) для dyn Trait
. Когда я использую оператор равенства для Box<dyn Trait>
, Rc<dyn Trait>
или Arc<dyn Trait>
, это приводит к перемещению правильного операнда, а не заимствованию. Почему? Помимо прочего, это предотвращает использование assert_eq!
непосредственно для таких значений.
В документации для операторов сравнения говорится:
В отличие от арифметических и логических операторов, приведенных выше, эти операторы неявно берут общие заимствования своих операндов, оценивая их в контексте выражения:
a == b;
// is equivalent to
::std::cmp::PartialEq::eq(&a, &b);
Это означает, что операнды не нужно выносить.
Я не могу понять, почему в этом случае перемещается правильный операнд. Разве правильный операнд не должен оказаться в методе eq
как &Box<dyn Trait>
(т. е. заимствование)?
Репродукция: (Детская площадка)
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
}
Я нашел несколько относительно простых обходных путей:
left.eq(&right)
(хотя для assert_eq!
это не помогает)*left == *right
&left == &right
&*left == &*right
Option
: Some(left) == Some(right)
(полезность ограничена, поскольку значения все равно перемещаются - в Option
)Но (а) их необходимость немного неудовлетворительна, и (б) мне все равно хотелось бы понять, почему это происходит.
Это ошибка компилятора: проблема #31740.
Пока ошибка не будет исправлена, вам придется ее обойти. Любой из ваших вариантов работает, хотя я бы определенно выбрал тот, который не меняет аргументы.
Я почти уверен, что это ошибка компилятора.
left == right
должен автоматически брать ссылки при необходимости. Например, еслиleft
иright
равныBox<String>
, ваш пример компилируется, хотя каждыйBox<T>
так же некопируем, как иBox<dyn Trait>
...