Как предложил Питер Холл в комментариях, самое простое решение - вывести Copy
для вашего сложного типа и реализовать операции для значений. Для реализаций ref-ref и реализаций ref-val вы можете просто разыменовать ссылки и использовать реализацию val-val.
Если вы хотите использовать подход, с которого вы начали работать, вам нужны границы черт более высокого ранга:
use std::ops::*;
#[derive(Clone, PartialEq)]
pub struct Complex<T: Clone> {
pub re: T,
pub im: T,
}
// Ref-Ref Multiplication
impl<'a, 'b, T: Clone> Mul<&'b Complex<T>> for &'a Complex<T>
where
T: Add<T, Output = T>,
T: Sub<T, Output = T>,
&'a T: Add<&'b T, Output = T>,
&'a T: Mul<&'b T, Output = T>,
&'a T: Sub<&'b T, Output = T>,
{
type Output = Complex<T>;
fn mul(self, rhs: &'b Complex<T>) -> Complex<T> {
Complex {
re: &self.re * &rhs.re - &self.im * &rhs.im,
im: &self.re * &rhs.im + &self.im * &rhs.re,
}
}
}
// Ref-Value Multiplication
impl<'a, T: Clone> Mul<Complex<T>> for &'a Complex<T>
where
T: Add<T, Output = T>,
T: Sub<T, Output = T>,
&'a T: for<'b> Add<&'b T, Output = T>,
&'a T: for<'b> Mul<&'b T, Output = T>,
&'a T: for<'b> Sub<&'b T, Output = T>,
{
type Output = Complex<T>;
fn mul(self, rhs: Complex<T>) -> Complex<T> {
let t = &rhs;
self.mul(t)
}
}
В вашей версии время жизни 'b
в реализации ref-value выбирается пользователем черты. Поскольку пользователь может использовать любое время жизни для 'b
, rhs
потребуется статическое время жизни для того, чтобы ваш код был действительным. Вместо этого вы хотите, чтобы *'a T
удовлетворял заданным границам черты для любого данного времени жизни 'b
, что и является тем, для чего предназначены HRTB.
Альтернативный, менее повторяющийся способ написания границ признаков для второй реализации:
impl<'a, T: Clone> Mul<Complex<T>> for &'a Complex<T>
where
Self: for<'b> Mul<&'b Complex<T>, Output = Complex<T>>,
{
type Output = Complex<T>;
fn mul(self, rhs: Complex<T>) -> Complex<T> {
self.mul(&rhs)
}
}