Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I do C++'s SFINAE in Rust?

Tags:

rust

I was wondering about this code:

#[inline(always)]
pub fn multiply_u128<T>(a: T, b: T, result: &mut[u64])  {
    let r = a as u128 * b as u128;
    //fill result with 64 bit chunks
}

it will work for integers except when T is u128. In this case we could get overflows. Is it possible to make this function work with u8, u16, u32, u64 but not u128?

like image 735
Guerlando OCs Avatar asked Jan 24 '23 05:01

Guerlando OCs


2 Answers

Can I do C++'s SFINAE in Rust?

No, Rust's generics are resolved quite a bit differently than in C++.


The most suitable way to implement this would be with Into<u64>:

pub fn multiply_u128<T: Into<u64>>(a: T, b: T, result: &mut[u64])  {
    let r = a.into() as u128 * b.into() as u128;
    // fill result with 64 bit chunks
}

Playground. Also, if you're up to using nightly features, consider using u64::widening_mul.

like image 177
kmdreko Avatar answered Jan 30 '23 09:01

kmdreko


You could introduce a trait for this purpose and only implement it for the relevant types.

pub trait NotBigUInt {
    fn as_u128(self) -> u128;
}

impl NotBigUInt for u8 {
    fn as_u128(self) -> u128 {
        self as u128
    }
}
impl NotBigUInt for u16 {
    fn as_u128(self) -> u128 {
        self as u128
    }
}
impl NotBigUInt for u32 {
    fn as_u128(self) -> u128 {
        self as u128
    }
}
impl NotBigUInt for u64 {
    fn as_u128(self) -> u128 {
        self as u128
    }
}

#[inline(always)]
pub fn multiply_u128<T: NotBigUInt>(
    a: T,
    b: T,
    result: &mut [u64],
) {
    let r = a.as_u128() * b.as_u128();
    //fill result with 64 bit chunks
    result[0] = r as u64;
}

fn main() {
    let mut r = [0_u64; 1];
    multiply_u128(1_u8, 1_u8, &mut r);
    multiply_u128(1_u16, 1_u16, &mut r);
    multiply_u128(1_u32, 1_u32, &mut r);
    multiply_u128(1_u64, 1_u64, &mut r);
    // multiply_u128(1_u128, 1_u128, &mut r);
}
like image 26
prog-fh Avatar answered Jan 30 '23 08:01

prog-fh