Let's say I want to abstract over different bigint implementations (like given by the crates num
, ramp
and rust-gmp
). I could come up with the following trait:
trait Integer:
Sized + Eq + Ord + Clone
+ Add<Output=Self> + Sub<Output=Self> + Mul<Output=Self> + Div<Output=Self> + Rem<Output=Self> + Neg<Output=Self>
+ From<u64> + Hash
{
fn zero() -> Self;
fn one() -> Self;
fn invert(&self, modulus: &Self) -> Option<Self>;
fn powm(&self, exp: &Self, modulus: &Self) -> Self;
}
Unfortunately, this is not enough. I also need that the basic arithmetic operations are implemented for references as well. Things like &a + b
, a + &b
and &a + &b
have to work. This leads me to trait bounds like the following on every function:
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct GcdResult<T> {
/// Greatest common divisor.
gcd: T,
/// Coefficients such that: gcd(a, b) = c1*a + c2*b
c1: T, c2: T,
}
/// Calculate greatest common divisor and the corresponding coefficients.
fn extended_gcd<T: Integer + Clone>(a: T, b: T) -> GcdResult<T>
where
for<'a, 'b> &'a T: Add<&'b T, Output=T>,
for<'a> &'a T: Add<T, Output=T>,
for<'a> T: Add<&'a T, Output=T>,
for<'a, 'b> &'a T: Sub<&'b T, Output=T>,
for<'a> &'a T: Sub<T, Output=T>,
for<'a> T: Sub<&'a T, Output=T>,
for<'a, 'b> &'a T: Mul<&'b T, Output=T>,
for<'a> &'a T: Mul<T, Output=T>,
for<'a> T: Mul<&'a T, Output=T>,
for<'a, 'b> &'a T: Div<&'b T, Output=T>,
for<'a> &'a T: Div<T, Output=T>,
for<'a> T: Div<&'a T, Output=T>,
for<'a, 'b> &'a T: Rem<&'b T, Output=T>,
for<'a> &'a T: Rem<T, Output=T>,
for<'a> T: Rem<&'a T, Output=T>,
for<'a> &'a T: Neg<Output=T>,
{
// Euclid's extended algorithm
let (mut s, mut old_s) = (T::zero(), T::one());
let (mut t, mut old_t) = (T::one(), T::zero());
let (mut r, mut old_r) = (b, a);
while r != T::zero() {
let quotient = &old_r / &r;
let mut tmp;
tmp = r.clone(); r = old_r - "ient * r; old_r = tmp;
tmp = s.clone(); s = old_s - "ient * s; old_s = tmp;
tmp = t.clone(); t = old_t - quotient * t; old_t = tmp;
}
let _quotients = (t, s); // == (a, b) / gcd
GcdResult { gcd: old_r, c1: old_s, c2: old_t }
}
These trait bounds have to be repeated for every function that is generic over Integer
. Is there a way to include the constraints in the trait definition? Can it be automatized using macros?
If you want to play with the example given here, you can find the full code on Github.