Poperton
Poperton

Reputation: 2127

Can I do C++'s SFINAE in 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?

Upvotes: 4

Views: 562

Answers (2)

kmdreko
kmdreko

Reputation: 59962

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.

Upvotes: 6

prog-fh
prog-fh

Reputation: 16815

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);
}

Upvotes: 2

Related Questions