rustの演算子オーバーロードをする際、値と借用の両方の実装を行う方法です。
次の構造体の掛け算を考えます。
#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Unit { pub coef: BigInt, pub xpow: BigInt, pub ypow: BigInt, }
値の掛け算と借用の掛け算のテストを先に書いておきます。
#[test] fn unit_test() { let u2 = Unit { coef: BigInt::from(4), .. Default::default() }; let u3 = Unit { coef: BigInt::from(3), .. Default::default() }; assert_eq!((u2.clone() * u3.clone()).to_string(), "12"); // 値 assert_eq!((&u2 * &u3).to_string(), "12"); // 借用 }
値の掛け算は次のように実装します。
use std::ops::Mul; impl Mul for Unit { type Output = Unit; fn mul(self, other: Self) -> Self { Unit { coef: &self.coef * &other.coef, xpow: &self.xpow + &other.xpow, ypow: &self.ypow + &other.ypow, } } }
これだけだと、借用の掛け算の実装がないので、以下のエラーが表示されます。
error[E0369]: binary operation `*` cannot be applied to type `&unit::Unit` --> src/unit.rs:236:21 | 236 | assert_eq!((&u2 * &u3).to_string(), "12"); | --- ^ --- &unit::Unit | | | &unit::Unit | = note: an implementation of `std::ops::Mul` might be missing for `&unit::Unit`
借用バージョンの実装を追加します。
impl<'a> Mul<&'a Unit> for &'a Unit { type Output = Unit; fn mul(self, other: Self) -> Unit { Unit { coef: &self.coef * &other.coef, xpow: &self.xpow + &other.xpow, ypow: &self.ypow + &other.ypow, } } }
これで実装はできましたが、実装の中身はほぼ同じなので、これはマクロを使って一つにまとめられるはずですが、
やり方が分かりません。
なお、「左側が値、右側が借用」「左側が借用、右側が値」もあった方がいいですが、これもマクロでできる気がするのですが。。
impl_ops クレートを使う
impl_ops クレートを使うと、マクロで全てのパターンを実装してくれるようです。
impl_op_ex!(* |a: &Unit, b: &Unit| -> Unit { Unit { coef: &a.coef * &b.coef, xpow: &a.xpow + &b.xpow, ypow: &a.ypow + &b.ypow, } });