user124784
user124784

Reputation: 956

Implementing mean function for generic types

I'm trying to write a function to return the mean of a Vector. I want it to work with generic types but I'm having some difficulty implementing it.

extern crate num; // 0.2.0

use num::Zero;
use std::ops::{Add, Div};

pub struct Vector<T> {
    pub size: usize,
    pub data: Vec<T>,
}

impl<T: Copy + Zero + Add<T, Output = T>> Vector<T> {
    pub fn sum(&self) -> T {
        self.data.iter().fold(T::zero(), |sum, &val| sum + val)
    }
}

impl<T: Copy + Zero + Add<T, Output = T> + Div<T, Output = T>> Vector<T> {
    pub fn mean(&self) -> T {
        let sum = self.sum();
        sum / self.data.len()
    }
}

Playground.

The above example doesn't compile as self.data.len() is a usize and sum is of type T:

error[E0308]: mismatched types
  --> src/lib.rs:20:15
   |
20 |         sum / self.data.len()
   |               ^^^^^^^^^^^^^^^ expected type parameter, found usize
   |
   = note: expected type `T`
              found type `usize`

I know I could change the signature to:

impl<T: Copy + Zero + Add<T, Output = T> + Div<usize, Output = T>> Vector<T>

It would compile - but this isn't implemented for the Rust primitive types. How should I go about this?

Upvotes: 5

Views: 725

Answers (2)

Shepmaster
Shepmaster

Reputation: 431619

In many cases, it doesn't make sense to compute an average of the input type. For example, the integer average of the integers 1 and 2 is 1. If you want to be able to get the average of any iterator of values that can be treated as an i64 and get the average as a f64, you can use Iterator::sum and Into:

fn avg_iter<I>(s: I) -> f64
where
    I: IntoIterator,
    I::Item: Into<i64>,
{
    let mut count = 0;
    let total = s
        .into_iter()
        .map(Into::into)
        .inspect(|_| count += 1)
        .sum::<i64>();
    total as f64 / count as f64
}

Some examples:

use std::collections::VecDeque;

fn main() {
    let a: Vec<u16> = vec![1, 2, 3];
    let b: Vec<u32> = vec![1, 2, 3];
    let c: VecDeque<u16> = vec![1u16, 2, 3].into();

    let v = avg_iter(a.iter().cloned());
    println!("{}", v);
    let v = avg_iter(a);
    println!("{}", v);

    let v = avg_iter(b.iter().cloned());
    println!("{}", v);
    let v = avg_iter(b);
    println!("{}", v);

    let v = avg_iter(c.iter().cloned());
    println!("{}", v);
    let v = avg_iter(c);
    println!("{}", v);
}

Upvotes: 0

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65832

The primitive types implement the FromPrimitive trait, defined in the num crate, to allow conversions between primitive types, including usize. We can add a FromPrimitive bound on the function, and then we can convert the usize to a T:

extern crate num; // 0.2.0

use num::{FromPrimitive, Zero};
use std::ops::{Add, Div};

impl<T> Vector<T>
where
    T: Copy + Zero + Add<T, Output = T> + Div<T, Output = T> + FromPrimitive,
{
    pub fn mean(&self) -> T {
        let sum = self.sum();
        sum / FromPrimitive::from_usize(self.data.len()).unwrap()
    }
}

Upvotes: 6

Related Questions