Почему оператор равенства для принадлежащего объекта типажа перемещает правый операнд?

У меня есть черта, в которой я реализовал PartialEqEq) для 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)

Но (а) их необходимость немного неудовлетворительна, и (б) мне все равно хотелось бы понять, почему это происходит.

Я почти уверен, что это ошибка компилятора. left == right должен автоматически брать ссылки при необходимости. Например, если left и right равны Box<String>, ваш пример компилируется, хотя каждый Box<T> так же некопируем, как и Box<dyn Trait>...

BallpointBen 09.04.2024 04:24
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
6
1
109
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это ошибка компилятора: проблема #31740.

Пока ошибка не будет исправлена, вам придется ее обойти. Любой из ваших вариантов работает, хотя я бы определенно выбрал тот, который не меняет аргументы.

Другие вопросы по теме