Home > Blockchain >  Generic function that accepts Vec<u32> and Vec<f32>
Generic function that accepts Vec<u32> and Vec<f32>

Time:09-14

I want to define a function to calculate the median of a vector that contains either f32 or u32 values, or possibly all numeric types. f32 and u32 are my current use cases.

I don't think I understand generics or traits sufficiently. The best I could do is below, where I specify that the generic type parameter T needs to implement some traits to allow mathematical operations. If possible I would also like to cast the return type to be f32 rather than T.

fn median<T: Add<Output = T>   From<f32>   Div<Output = T>>(array: &Vec<T>) -> T {
    if (array.len() % 2) == 0 {
        let ind_left = array.len() / 2 - 1;
        let ind_right = array.len() / 2;
        (array[ind_left]   array[ind_right]) / 2.0
    } else {
        array[(array.len() / 2)]
    }
}

But this has a problem on line 5:

mismatched types
expected type parameter `T`
             found type `{float}`rustcE0308

I could use map to convert the vector of one type to the other, but that feels like unnecessary work.

CodePudding user response:

The first problem is that 2.0 is a float literal, it's not a T. T can be anything which implements the specified traits.

So you need to convert 2.0 to T for it to work:

(array[ind_left]   array[ind_right]) / T::from(2.0)

Then you'll have a second problem, which is that array[ind_right] tries to copy the item out, but you have not told that the object can be copied. This requires an additional bound of Copy (or Clone then an explicit Clone call).

If possible I would also like to cast the return type to be f32 rather than T.

Then you need to add a type bound of Into<f32>.

In fact I don't think the From<f32> is correct or useful: that lets you convert 2.0, but at the end of the day the only things convertible from f32 are f32 and f64.

Probably more useful would be the abiliy to convert to an f32, then working entirely on f32s internally. That means you need no other trait bound than that (and Copy):

fn median<T: Into<f32>   Copy>(array: &[T]) -> f32 {
    if (array.len() % 2) == 0 {
        let ind_left = array.len() / 2 - 1;
        let ind_right = array.len() / 2;
        (array[ind_left].into()   array[ind_right].into()) / 2.0
    } else {
        array[(array.len() / 2)].into()
    }
}

That's still not great tho, because Rust is very conservative in its implementation of From: only u8, i8, u16, and i16 are convertible that way, because a single-precision float (f32) only has 24 bits of precision, meaning it can't represent every 32 bit integer.

And casting is not generic in Rust.

So you probably want to check out num, it might have number-conversion traits which work for this sort of scenario.

  • Related