I'm learning Rust and want to understand how I can pass a vector of numbers (any kind) to a function. This code does not compile, but I can't figure out how to fix it:
use std::ops::{Add, Div};
fn average<T: Add + Div>(numbers: &Vec<T>) -> T {
let sum: T = numbers.iter().fold(0, |acc, n| acc + n);
sum / numbers.len()
}
fn main() {
let numbers: Vec<f64> = vec![1.0, 2.0, 3.0, 4.0];
println!("average2 = {}", average(&numbers));
}
I understand I can use numbers.iter().sum(), but I want to learn about fold.
The compiler complains about:
You can use Default trait to get zero value (most of the time). See also num-traits for more traits you can use.
When you say it's a type T: Add + Div Rust assumes it can be any type that implements them, including types created in the future that don't exist yet. It could be a matrix. It could be a string. It could be an image. There is nothing that limits Add or Div to numbers.
You have to declare absolutely every operation you could possibly use on the type, so use T: Num + Default to have Default available.
len() gives you usize, but there's no guarantee that T can be divided by usize. Rust doesn't have implicit numeric conversions, so only usize can be divided by usize.
Theoretically you can add T: From<usize> and use T::from(numbers.len()), but due to portability concerns very few data types can be converted from usize.
Instead of returning T you could return f64 and use num-traits' trait for casting types to f64 and divide it as a concrete type.
But Rust's generics are a very poor fit for numeric code. On every step Rust will make you ask "but what if the T is an array or a string or a file handle? How would I divide a friggin file handle by usize!?"
Consider using macros. They're not type-safe and closer to templates in C++, so you don't have to declare as much (but you will still need to cast usize to whatever type you divide)
Consider using concrete types. Do you really need to support every possible type?
Consider using type aliases, e.g. type MyNum = f64. That makes it slightly easier to switch to another type if you change your mind later.