8

I'm trying to solve an exercise at the end of this chapter in the Rust Book.

Here is a code sample:

fn mean(v: &Vec<i32>) -> f64 {
    let mut sum = 0.0;
    let mut count = 0.0;

    for val in v {
        sum += &f64::from(val);
        count += 1.0;
    }

    sum / count
}

fn main() {
    let v = vec![1, 2, 3, 4];

    println!("The mean is {}", mean(&v));
}

The error is:

error[E0277]: the trait bound `f64: std::convert::From<&i32>` is not satisfied
 --> src/main.rs:6:17
  |
6 |         sum += &f64::from(val);
  |                 ^^^^^^^^^ the trait `std::convert::From<&i32>` is not implemented for `f64`
  |
  = help: the following implementations were found:
            <f64 as std::convert::From<f32>>
            <f64 as std::convert::From<i16>>
            <f64 as std::convert::From<i32>>
            <f64 as std::convert::From<i8>>
          and 3 others
  = note: required by `std::convert::From::from`

I also tried using the as keyword but it didn't help.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • [Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?](https://stackoverflow.com/q/40006219/155423) – Shepmaster Jun 24 '20 at 17:50

2 Answers2

17

f64 only implements From for i32, not &i32 (which is a reference to an i32). To get this to work, you will need to dereference val.

fn mean(v: &Vec<i32>) -> f64 {
    let mut sum = 0.0;
    let mut count = 0.0;

    for val in v {
        sum += f64::from(*val);
        count += 1.0;
    }

    sum / count
}

The same applies if you try to do val as f64, and in fact, you get a much more helpful error message in that case:

error[E0606]: casting `&i32` as `f64` is invalid
 --> src/main.rs:6:16
  |
6 |         sum += val as f64;
  |                ---^^^^^^^
  |                |
  |                cannot cast `&i32` as `f64`
  |                help: dereference the expression: `*val`
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Joe Clay
  • 33,401
  • 4
  • 85
  • 85
  • This is great I totally forgot about this. Thank you very much – Artsiom Shamsutdzinau Mar 09 '19 at 17:38
  • mark the answer as correct, it's benefficial for both! (and google search) : ) – aran Mar 09 '19 at 17:42
  • 1
    Also note that you could change the type of `v` to `&[i32]` (a [slice](https://doc.rust-lang.org/book/ch04-03-slices.html) of `i32`s) - that's more idiomatic in Rust than taking a reference to a `Vec`, as it makes the function more flexible. For example, it would allow you to pass in a subsection of a `Vec`, rather than always processing the entire thing. – Joe Clay Mar 09 '19 at 21:11
3

You can dereference the variable with *val as f64.

fn mean(v: &Vec<i32>) -> f64 {
    let mut sum = 0.0;
    let mut count = 0.0;

    for val in v {
        sum += *val as f64;
        count += 1.0;
    }

    sum / count
}

fn main() {
    let v = vec![1, 2, 3, 4];

    println!("The mean is {}", mean(&v));
}

Another way to do it

fn main() {
    let v = vec![1, 2, 3, 4];
    let mean: f64 = v.iter().map(|&val| val as f64).sum::<f64>() / v.len() as f64;

    println!("The mean is {}", mean);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Nice example, I am learning language, if I understood then `v.iter().map(|&val| val as i64).sum::() as f64 / v.len() as f64` would be even better – Daniele Cruciani Nov 07 '22 at 07:14