Reputation: 956
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()
}
}
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
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
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