1

I tried to implement a small module where I calculate the mean of a vector:

pub mod vector_calculations {
    pub fn mean(vec: &Vec<i32>) -> f32 {
        let mut sum: f32 = 0.0;

        for el in vec.iter() {
            sum = el + sum;
        }

        sum / vec.len()
    }
}

As far as I can tell from the compiler error, there are two problems with my code:

error[E0277]: the trait bound `&i32: std::ops::Add<f32>` is not satisfied
 --> src/main.rs:6:22
  |
6 |             sum = el + sum;
  |                      ^ no implementation for `&i32 + f32`
  |
  = help: the trait `std::ops::Add<f32>` is not implemented for `&i32`

error[E0277]: the trait bound `f32: std::ops::Div<usize>` is not satisfied
 --> src/main.rs:9:13
  |
9 |         sum / vec.len()
  |             ^ no implementation for `f32 / usize`
  |
  = help: the trait `std::ops::Div<usize>` is not implemented for `f32`

I'm trying to add a &i32 with a f32 and I'm trying to divide a f32 with an usize.

I could solve the second error by changing the last line to:

sum / (vec.len() as f32)

Is this is actually how a Rust programmer would do this?

Furthermore, I don't really know how to solve the first error. What has to be done and why?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Chris
  • 1,008
  • 2
  • 11
  • 22
  • 1
    Quick notes: 1. An `i32` can hold values greater than `f32` can represent accurately, so you probably want to use `f64`. 2. You're adding an unknown number of values to this, so you could overflow `sum`, so you should really use an approach that will work, e.g. https://stackoverflow.com/a/1934266/785065 – loganfsmyth Jul 25 '17 at 17:29

2 Answers2

4

Yes, dereferencing values and converting numeric types is normal in Rust. These conversions help the programmer recognize that edge cases are possible. As loganfsmyth points out:

An i32 can hold values greater than f32 can represent accurately

Unfortunately, the compiler can't tell what's "correct" for your case, so you still have to be on guard.

For what it's worth, I'd write your current implementation using Iterator::sum:

fn mean(items: &[i32]) -> f64 {
    let sum: f64 = items.iter().map(|&v| v as f64).sum();
    sum / (items.len() as f64)
}

You should also probably handle the case where the input is empty to avoid dividing by zero:

fn mean(items: &[i32]) -> Option<f64> {
    let len = items.len();

    if len == 0 {
        None
    } else {
        let sum: f64 = items.iter().map(|&v| v as f64).sum();
        Some(sum / (len as f64))
    }
}

Using the method from What is a good solution for calculating an average where the sum of all values exceeds a double's limits?, but made a bit more iterator-heavy:

fn mean2(ary: &[i32]) -> f64 {
    ary.iter().enumerate().fold(0.0, |avg, (i, &x)| {
        avg + ((x as f64 - avg) / (i + 1) as f64)
    })
}

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
2

.iter() returns an &i32 and Rust does not automatically dereference for type conversions — you are currently trying to change the pointer (&) instead of changing what it's pointing to.

Changing your code to look like this is the simplest way to make it work:

pub mod vector_calculations {
     pub fn mean(vec: &Vec<i32>) -> f32 {
        let mut sum: f32 = 0.0;

        for el in vec.iter() {
            sum = *el as f32 + sum; // first dereference the pointer, than cast to f32
        }

        sum / vec.len() as f32 // cast to f32
    }
}

But there are some ways to improve this kind of code:

pub mod vector_calculations {
     pub fn mean(vec: &[i32]) -> f32 { // accept a slice instead of a vector
                                       // it now allows arrays, slices, and vectors
                                       // but now you can't add or remove items 
                                       // during this function call.

        let mut sum: i32 = 0;   // as the sum is still a whole number, changing the type
                                // should make it slightly easier to understand.

        for el in vec.iter() {
            sum = el + sum;     // now this works without changing the type of el
                                // you don't even need to dereference el anymore
                                // as Rust does it automatically.
        }

        sum as f32 / vec.len() as f32 // now you need to cast to f32 twice at the end
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
lncr
  • 826
  • 8
  • 16