Как реализовать операторы для структуры Rust, когда операнды могут быть значениями или ссылками?

Я новичок в этом и пытаюсь перегрузить достаточно операторов (Mul, Sub), чтобы получить простую арифметическую функцию для простого типа для компиляции:

#[derive(Debug)]
struct Tuple {
    x: f64, y: f64, z: f64, w: f64,
}

fn dot(a: &Tuple, b: &Tuple) -> f64 {
    (a.x * b.x) + (a.y * b.y) + (a.z * b.z) + (a.w * b.w)
}


impl std::ops::Mul<f64> for Tuple {
    type Output = Tuple;
    fn mul(self, rhs: f64) -> Tuple {
        Tuple{x: self.x * rhs, y: self.y * rhs,
              z: self.z * rhs, w: self.w * rhs}
    }
}

impl std::ops::Mul<f64> for &Tuple {
    type Output = Tuple;
    fn mul(self, rhs: f64) -> Tuple {
        Tuple{x: self.x * rhs, y: self.y * rhs,
              z: self.z * rhs, w: self.w * rhs}
    }
}

impl std::ops::Sub<&Tuple> for Tuple {
    type Output = Tuple;
    fn sub(self, rhs: &Tuple) -> Tuple {
        Tuple{x: self.x - rhs.x, y: self.y - rhs.y,
              z: self.z - rhs.z, w: self.w - rhs.w}
    }
}

impl std::ops::Sub<&Tuple> for &Tuple {
    type Output = Tuple;
    fn sub(self, rhs: &Tuple) -> Tuple {
        Tuple{x: self.x - rhs.x, y: self.y - rhs.y,
              z: self.z - rhs.z, w: self.w - rhs.w}
    }
}

fn reflect(inc: &Tuple, normal: &Tuple) -> Tuple {
    //inc - normal * 2.0 * inc.dot(&normal)
    let a = dot(&inc, &normal);
    let b = 2.0 * a;
    let c = normal * b;
    inc - c    // <--- compile error: expected `&Tuple`, found struct `Tuple`
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn something() {
        let v = Tuple { x: 1.0, y: -1.0, z: 0.0, w: 0.0 };
        let n = Tuple { x: 0.0, y: 1.0, z: 0.0, w: 0.0 };

        //let r = reflect(&v, &n);
        let r = v - n * 2.0 * dot(&v, &n);  // <-- compile error: expected `&Tuple`, found struct `Tuple`
//                  ~~~~~~~~~~~~~~~~~~~~~        

        println!("{:?}", r);
    }
}

Детская площадка ржавчины

Проблема в том, что я просто не могу понять все различные реализации, которые мне нужно определить - кажется, что существуют отдельные impl функции, необходимые не только для значений, но и для ссылок на значения. И как только я реализую достаточное количество из них, я, кажется, получаю ссылку вместо значения:

error[E0308]: mismatched types
  --> lib/tuple.rs:70:11
   |
70 |     inc - c
   |           ^
   |           |
   |           expected `&Tuple`, found struct `Tuple`
   |           help: consider borrowing here: `&c`

Я недоумеваю, почему я не могу написать что-то настолько простое — что я упускаю?

Что еще более тревожно, если я заставлю это работать, действительно ли мне нужно будет реализовывать огромное количество функций не только для каждого оператора, но и для каждой комбинации?

  • A @ B
  • A @ &B
  • &A @ B
  • &A @ &B

Можно ли автоматически разыменовать ссылку в значение и использовать версию значения операторной функции? В конце концов, это ссылки на неизменяемые значения — значения прямо здесь.

(Я не был уверен, стоит ли упоминать об этом, но на самом деле я использовал use derive_more::{Mul}; для нового типа, оборачивая другой тип кортежа, но derive_more, похоже, включает только поддержку автоматической генерации функций бинарного оператора «оба являются значениями», а не другие три, включающие одну или две ссылки, поэтому в этом вопросе я вернулся к основному struct, где я вижу ту же проблему).

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Одним из способов справиться с этим было бы использование макросов, что довольно часто делает стандартная библиотека для случаев, подобных вашему.

В вашем случае вы можете сделать, например:

macro_rules! tuple_mul {
    ( $lhs:ty , $rhs:ty ) => {
        impl std::ops::Mul<$rhs> for $lhs {
            type Output = Tuple;
            fn mul(self, rhs: $rhs) -> Tuple {
                Tuple{x: self.x * rhs, y: self.y * rhs,
                      z: self.z * rhs, w: self.w * rhs}
            }
        }
    }
}

tuple_mul!(Tuple, f64);
tuple_mul!(Tuple, &f64);
tuple_mul!(&Tuple, f64);
tuple_mul!(&Tuple, &f64);

И аналогично для других операторов. Таким образом, вы можете избежать копирования избыточного кода, вместо этого используя макросы, чтобы сгенерировать его для вас и сделать ваш код более удобным в сопровождении.

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