Working with usize, f64

I'm writing two versions of a trivial method to compute an average for an integer slice, and return an integer, and a second version that returns a float.

The first version, that returns an integer average, has this code:

fn compute_average(list: &[i32]) -> i32 {
  let sum_of_items: i32 = list.iter().sum();
  let count_of_items: i32 = i32::try_from(list.len()).unwrap();
  let average = sum_of_items / count_of_items;


My question about the above:
a) why do some parts of Rust, like .len() return a usize while others, like sum() return an integer? It seems like if Rust wants to have a "soft size" that's platform dependent (like usize/isize) it should use it everywhere a count of length of something is returned.
b) is my try_from approach reasonable? I mean, is there a more direct way to do what I'm trying to do?

The float version has its own problems/questions:

fn compute_average(list: &[i32]) -> f64 {
    let sum_of_items = f64::from(list.iter().sum());
    let count_of_items = f64::try_from(i32::try_from(list.len()).unwrap()).unwrap();
    let average = sum_of_items  / count_of_items;


a) Initially I tried to f64::try_from(list.len()) but apparently that's not supported for usize (!?) but I did conclude that I could first convert the list.len() to i32 and then to f64, but that yields a pretty ugly piece of code - is there a better way to do this?
b) the first line "let sum_of_items" ... is erroring out that the compiler cannot infer the type T. I'm confused about this - what can't it infer, and how should I tell it what it wants to know? I tried casting "list.iter().sum()" as something like an i32, thinking that that would bring some clarity, but it didn't help either.

Thanks for any insights!
Always Learning.

Tip: you can format your code blocks using triple backticks, like

code goes here

The sum method has a generic return type - basically, you have to tell it what you want it to return; you can return any type as long as it implements std::iter::Sum<T> for whatever type T you're iterating over. In your first example, you're telling the compiler that you want sum to return an i32 by annotating let sum_of_items with that type. That's why sum isn't giving a usize (though in this case it can't - it doesn't make sense to sum a set of potentially negative integers into a non-negative integer, so there's no implementation of Sum<i32> for usize.)
The float function doesn't compile since both the return type of sum and the argument type of f64::try_from are generic, there could potentially be any number of intermediate types, so it needs disambiguation, which you could do by annotating the call to sum:

let sum_of_items = f64::from(list.iter().sum::<i32>());

There isn't a reason to use try_from here; you can just use as.

fn compute_average(list: &[i32]) -> i32 {
    let sum: i32 = list.iter().sum();
    let count = list.len() as i32;
    sum / count

Combining the above, your average function could be written as:

fn compute_average(list: &[i32]) -> f64 {
    let sum = list.iter().sum::<i32>() as f64;
    let count = list.len() as f64;
    sum / count

I would say that the try_from() version is safer: it will panic upon overflow, instead of silently truncating the length.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.