I'm trying to write a function which takes a slice of numbers and calculates the mean.
I tried using the ideas from Implementing mean function for generic types but get an error.
My code is:
extern crate num;
use num::{FromPrimitive, Zero};
use std::ops::{Add, Div};
fn main() {
let mut numbers = [10, -21, 15, 20, 18, 14, 18];
let err = "Slice is empty.";
println!("Mean is {:.3}", mean(&numbers).expect(err));
}
fn mean<T>(numbers: &[T]) -> Option<f64>
where
T: Copy + Zero + Add<T, Output = T> + Div<T, Output = T> + FromPrimitive,
{
match numbers.len() {
0 => None,
_ => {
let sum = numbers.iter().sum: ();
let length = FromPrimitive::from_usize(numbers.len()).unwrap();
Some(sum / length)
}
}
}
The error is:
error[E0658]: type ascription is experimental (see issue #23416)
--> src/main.rs:20:23
|
20 | let sum = numbers.iter().sum: ();
| ^^^^^^^^^^^^^^^^^^^^^^
Is there any way of writing a generic mean function without using experimental features?
Your type also needs to implement Sum<T>
, so you need to add the Sum
bound too. This is not enough, you also need to convert your generic type to a f64
. The num crate also has the ToPrimitive
trait to do this:
fn mean<'a, T: 'a>(numbers: &'a [T]) -> Option<f64>
where
T: ToPrimitive + Sum<&'a T>,
{
match numbers.len() {
0 => None,
_ => {
let sum = numbers.iter().sum::<T>();
FromPrimitive::from_usize(numbers.len())
.and_then(|length: f64| T::to_f64(&sum).and_then(|val| Some(val / length)))
}
}
}
Playground
The other answers will likely help you with your real problem of writing this function generically.
The actual error you've asked about though is just a syntax mistake. You wrote this:
let sum = numbers.iter().sum: ();
But almost certainly intended to write:
let sum = numbers.iter().sum();
The compiler has seen the :
that you have accidentally included, and thinks that you are trying to use type ascription. Type ascription is syntax to use type annotations inline within an expression, instead of just in variable declarations.
What you wrote is very similar to:
let sum: () = numbers.iter().sum;
If you were to enable type ascription in a nightly rustc build, the error would change because now the compiler will tell you that sum
is a function and definitely does not have type ()
.
How about this:
use std::iter::Sum;
fn main() {
let err = "Slice is empty.";
// Test vector of integers
let numbers = vec![10i32, -21, 15, 20, 18, 14, 18];
println!("Mean is {:.3}", mean(numbers.into_iter()).expect(err));
// Test vector of floating point numbers
let numbers = vec![10f64, -21f64, 15f64, 20f64, 18f64, 14f64, 18f64];
println!("Mean is {:.3}", mean(numbers.into_iter()).expect(err));
// Test empty vector
let numbers: Vec<i32> = Vec::new();
println!("Mean is {:.3}", mean(numbers.into_iter()).expect(err));
}
fn mean<T, I: Iterator<Item = T>>(iter: I) -> Option<f64>
where
T: Into<f64> + Sum<T>,
{
let mut len = 0;
let sum = iter
.map(|t| {
len += 1;
t
})
.sum::<T>();
match len {
0 => None,
_ => Some(sum.into() / len as f64)
}
}
Same code in the Rust Playground
It seems to have the following advantages over the answers posted so far:
- Much simpler generic type definition.
- No reliance on external
num
crate.
- No need for difficult-to-guess traits like
FromPrimitive
and Zero
.
- No manual lifetimes declarations.
Or this version which has the following differences to the one above:
- Can take arrays rather than vectors.
- Does not consume the array (or vector).
- Needs manual lifetime declarations.
use std::iter::Sum;
fn main() {
let err = "Slice is empty.";
// Test aray of integers
let numbers = [10, -21, 15, 20, 18, 14, 18];
println!("Mean is {:.3}", mean(numbers.iter()).expect(err));
// Test array of floating point numbers
let numbers = [10f64, -21f64, 15f64, 20f64, 18f64, 14f64, 18f64];
println!("Mean is {:.3}", mean(numbers.iter()).expect(err));
// Test empty array
let numbers: [i32; 0] = [];
match mean(numbers.iter()) {
Some(mean_) => println!("Mean is {:.3}", mean_),
None => println!("Empty array"),
}
}
fn mean<'a, T, I>(iter: I) -> Option<f64>
where
T: Into<f64> + Sum<&'a T> + 'a,
I: Iterator<Item = &'a T>,
{
let mut len = 0;
let sum = iter
.map(|t| {
len += 1;
t
})
.sum::<T>();
match len {
0 => None,
_ => Some(sum.into() / len as f64),
}
}
Thanks to my friend Sven for code contribution.