Право собственности на значения, переданные перегруженному PartialEq - PullRequest
1 голос
/ 02 февраля 2020

Я играл с перегрузкой некоторых операторов. Я столкнулся с ситуацией, которую я не совсем понимаю. При реализации признака PartialEq для моей структуры Value я заметил, что приведенная ниже реализация работает и не перемещает значения, что позволяет мне продолжать использовать значения после использования на них оператора == без передачи ссылок на значения в оператор.

С другой стороны, это не работает для реализации черты Neg (или Add, Sub и т. д. c.). Чтобы использовать оператор - без перемещения значения, я должен реализовать черту Neg для ссылок на структуру Value.

Почему я могу реализовать черту PartialEq, не беспокоясь о движении, когда не передается ссылка на значения, но при реализации черты Neg мне нужно беспокоиться? Я неправильно реализую черту Neg? Есть ли какая-то тонкость в черте PartialEq, которую я пропускаю?

Вот мой код:

struct Value {
    x: i32
}

impl PartialEq for Value {
    fn eq(&self, other: &Value) -> bool {
        if self.x == other.x {
                true
            } else {
                false
            }
    }
}
impl Eq for Value {}

impl Neg for &Value {
    type Output = Value;

    fn neg(self) -> Self::Output {
        Value {
            x: -self.x
        }
    }
}

fn main() {
    let v1: Value = Value {x: 1};
    let v2: Value = Value {x: 2};
    let equal = v1 == v2; // Not passing a reference, but also able to use v1
    let v3 = -&v1;
    let v4 = -&v1; // Works because I am passing a reference. If I change the implementation of Neg to 'impl Neg for Value' and remove the reference here and in the line above (for v3), it will complain that v1 had been moved (as expected).
}

1 Ответ

1 голос
/ 02 февраля 2020

Есть ли тонкость в черте PartialEq, которую я пропускаю? Методы

PartialEq принимают self и other по ссылке (&self и other: &T в подписи), в то время как Neg, Add, Sub и др. c. возьмите self и (для бинарных операторов) other по значению (self и other: T в подписи). v1 == v2 десугары в PartialEq::eq(&v1, &v2), в то время как !v1 десугары в Neg::neg(v1).

Причина, по которой вы можете захотеть Neg стать владельцем переданного значения, заключается в том, что значение выделено динамически c память (через Box, Vec, et c.). В этом случае может быть более эффективно мутировать self и затем возвращать self (или другой объект, повторно использующий динамическую память c в случае, когда тип Output отличается от типа Self) вместо этого выделения нового объекта (который потребует новых динамических c выделений памяти), даже если исходное значение не используется после операции.

С другой стороны, методы PartialEq всегда возвращают bool. bool не выделяет динамическую c память, поэтому нет никакой выгоды в передаче параметров по значению. Не ожидается, что для проверки того, что два объекта равны, потребуется мутировать одного или обоих объектов, поэтому параметры являются общими ссылками.

Я неправильно реализую черту Neg?

Нет, но вы можете рассмотреть возможность реализации Neg для Value и &Value (особенно если вы пишете библиотеку для использования другими). ​​

Если Ваш тип дешевый для копирования (т.е. он маленький и не использует динамическую c память), подумайте о реализации Clone и Copy (возможно, путем их получения). Таким образом, вы можете передавать значения операторам, не перемещая значение в оператор, поскольку вместо этого будет скопировано значение.

...