Home > OS >  How to restrict a generic type in a vector so that it can be converted to f32?
How to restrict a generic type in a vector so that it can be converted to f32?

Time:06-07

How can I make sure that the following function works for both integers and floats?

fn mean<T>(data: &Vec<T>) -> f32 {
    let sum = data.iter().sum::<T>() as f32;
    let count = data.len();
    return sum / count as f32;
}

I'm guessing I'd need to use some sort of bound or where clause to solve this, but I'm not sure about the syntax.

I've tried what the compiler suggests:

fn mean<T: std::iter::Sum<&T>>(data: &Vec<T>) -> f32 {...}

but I still get an error saying 'explicit lifetime name needed'.

I feel I'm missing something obvious here. Is there a way for instance to restrict T to types that can be cast to f32? If yes, that would (I assume) also satisfy the sum() function.

CodePudding user response:

So I assume that this is your current code:

fn mean<'a, T>(data: &'a [T]) -> f32
where
    T: std::iter::Sum<&'a T>,
{
    let sum = data.iter().sum::<T>() as f32;
    let count = data.len();
    return sum / count as f32;
}

Here the compiler correctly complains about the fact that T cannot be converted to an f32, because the where clause does not include that T has such a capability.

Sadly, there is no trait for "coercible to f32", as the as operator is a built-in operation.


You could, as you mentioned, use the From/Into traits to implement this function for every type that can be converted into an f32 via .into():

fn mean<'a, T>(data: &'a [T]) -> f32
where
    T: std::iter::Sum<&'a T>   Into<f32>,
{
    let sum = data.iter().sum::<T>().into();
    let count = data.len();
    return sum / count as f32;
}

fn main() {
    let v = vec![1.0, 2.0, 3.0, 4.0];
    println!("{}", mean(&v));
    let v2: Vec<i16> = vec![1, 2, 3, 4];
    println!("{}", mean(&v2));
}
2.5
2.5

Note, however, that Into<f32> is not implemented for all numeric types. For example, it is not implemented for i32, as i32 cannot be converted to an f32 without losing information.


If you still want to use any primitive type that can be converted to an f32 even if there are losses, you will have to use an external crate like the excellent num-traits. From it, you could use the AsPrimitive<f32> trait:

fn mean<'a, T>(data: &'a [T]) -> f32
where
    T: std::iter::Sum<&'a T>   num_traits::AsPrimitive<f32>,
{
    let sum = data.iter().sum::<T>().as_();
    let count = data.len();
    return sum / count as f32;
}

fn main() {
    let v = vec![1.0, 2.0, 3.0, 4.0];
    println!("{}", mean(&v));
    let v2: Vec<i32> = vec![1, 2, 3, 4];
    println!("{}", mean(&v2));
}
2.5
2.5

CodePudding user response:

If you follow the suggestions from the compiler further, you should arrive at something like <'a, T: 'a Sum<&T>>.

Then you notice that you can't use as because there's no trait for it. Substitute Into: let sum: f32 = data.iter().sum::<T>().into();

Some more compiler suggestions and you should arrive at: fn mean<'a, T: 'a std::iter::Sum<&'a T>>(data: &'a Vec<T>) -> f32 where f32: From<T>.

  • Related