Home > Software design >  Use saturating_add if available in Rust, fallback to regular addition for floats
Use saturating_add if available in Rust, fallback to regular addition for floats

Time:11-24

I am trying to write a function that is defined for both floats and integers.

For integers, I want to use saturating_add. For floats, there is no saturating_add function, but regular add is saturating (in the sense that infinity_value positive_value = infinity_value).

What I would like to write is

fn sat_add<T: num_traits::Num>(x: T, o: T) -> T {
  if implements(x, num_traits::ops::saturating::SaturatingAdd)
    x.saturating_add(o) // Saturating addition, if available
  else
    x   o // Fallback: regular addition
}

Note that T is static, and I want this to be inlined by the compiler. So its more of a pattern matching (trait present or not) rather than object-oriented programming.

(Note that I am currently using macros to implement either one or the other for various standard types, but that only covers the basic cases, something generic would be nicer than enumerating known data types.)

CodePudding user response:

You can define a trait and implement it for the desired types:

pub trait MyAdd {
    fn my_add(self, o: Self) -> Self;
}
macro_rules! my_add_int {
    ($($t:ty), ) => {$(
        impl MyAdd for $t {
            fn my_add(self, o:Self) -> Self {
                <$t>::saturating_add(self, o)
            }
        }
    )*}
}
macro_rules! my_add_float {
    ($($t:ty), ) => {$(
        impl MyAdd for $t {
            fn my_add(self, o:Self) -> Self {
                self   o
            }
        }
    )*}
}
my_add_int!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize);
my_add_float!(f32, f64);

playground

CodePudding user response:

Doing this the way you'd like to is not possible in current Rust because it requires specialization.

With specialization (available on nightly), your function could be implemented like this:

#![feature(specialization)]

use num_traits;
use std::ops::Add;

// A helper trait to implement `saturating_add`
pub trait SaturatingAdd {
    fn add(self, rhs: Self) -> Self;
}

impl<T: Add<Output = T>> SaturatingAdd for T {
    // the fallback implementation, defined for anything that implements
    // `T   T`, is marked "default"
    default fn add(self: T, rhs: T) -> T {
        self   rhs
    }
}

impl<T: num_traits::ops::saturating::SaturatingAdd> SaturatingAdd for T {
    // specialized implementation for types that define `saturating_add()`
    fn add(self: T, rhs: T) -> T {
        self.saturating_add(&rhs)
    }
}

// With the trait in place, the implementation of the function
// boils down to invoking the trait's only method.
pub fn saturating_add<T: SaturatingAdd>(a: T, b: T) -> T {
    a.add(b)
}

Playground

Unfortunately, it requires the full specialization feature, it doesn't work with the more modest min_specialization. In other words, don't expect this to become part of stable any time soon.

  • Related