Home > OS >  How do I use concrete values with generic numeric types?
How do I use concrete values with generic numeric types?

Time:10-04

Trying to write structs/impls that are generic over f32 and f64.

I'm using trait num_traits::float::Float as trait bounds.

But I get compiler errors when concrete values are used in the function, such as when an array is initialized or the length of the array (usize) is used. Wanting to cast concrete type to generic T, or something?? How do I handle this?

Thanks !

Example 1:

pub struct RingArray<T: Float, const N: usize> {
    tail: usize,  // Index of the most recently added element
    data: [T; N], // Array containing the data.
}

impl<T: Float, const N: usize> RingArray<T, N> {
    /// Creates a new RingArray of with length `length` and initialized to `init_value`.
    pub fn new() -> Self {
        Self {
            tail: N - 1,           // raw index to the last element
            // Initialize the data array to 0.0
            data: [0.0; N],  //  <-- ERROR. Compiler complains here about 0.0.  Expected type T found {float}
        }
    
    }
}

Example2:

pub struct MovingAverageFilter<T: Float, const N: usize> {
    ring_array: RingArray<T, N>,
    sum: T,
}
impl <T: Float, const N: usize> MovingAverageFilter<T, N> {
    pub fn push(&mut self, input: T) -> T {

        // Push the input and pop the head.
        let head = self.ring_array.push(input);

        // Add input to the sum and subtract the head
        self.sum = self.sum   input - head; 

        let length = self.ring_array.len();

        // Want to cast length to type T. How?
        self.sum/length  //  <-- ERROR. Expectded denom to be type T, found usize
    }
}

CodePudding user response:

0.0 is a literal that can't be used in a generic context. Instead, you can use Zero from num_traits, which is a trait for types that have a meaningful "zero" value:

use num_traits::Zero;

pub fn new() -> Self {
    Self {
        tail: N - 1, 
        // Initialize the data array to 0.0
        data: [Zero::zero(); N],
    }
}

For the second part, you are trying to divide a generic numeric type by a usize. You need to convert the usize into the float type first, which you can do by constraining the type by FromPrimitive from the same crate:

use num_traits::FromPrimitive;

impl <T: Float   FromPrimitive, const N: usize> MovingAverageFilter<T, N> {
    pub fn push(&mut self, input: T) -> T {

        // Push the input and pop the head.
        let head = self.ring_array.push(input);

        // Add input to the sum and subtract the head
        self.sum = self.sum   input - T::from_usize(head).unwrap(); 

        let length = self.ring_array.len();

        // Convert the usize to a T before dividing
        self.sum / T::from_usize(length).unwrap()
    }
}
  • Related