I have the following trait that I am trying to implement:
pub trait CentralMoment<Output = f32>
where
    Output: Copy,
{
    fn mean(&self) -> Output;
}
impl<T> CentralMoment for [T] {
    fn mean(&self) -> f32 {
        let sum: f32 = self.iter().sum();
        sum / self.len() as f32
    }
}
My issue is with the line let sum: f32 = self.iter().sum(). The compiler is telling me:
the trait bound `f32: Sum<&T>` is not satisfied
  --> src/lib.rs:45:36
   |
45 |         let sum: f32 = self.iter().sum();
   |                                    ^^^ the trait `Sum<&T>` is not implemented for `f32`
   |
help: consider extending the `where` bound, but there might be an alternative better way to express this requirement
   |
42 |     T: Copy, f32: Sum<&T>
but even when I try include f32: Sum<&T> I still get the same error.
What am I doing wrong here and how can I resolve this issue? Thanks in advance, and please let me know if you need any further clarification.
Arithmetic with generics is tricky. Getting all of the constraints and types right is like a game of whack-a-mole. Every time you fix an error another is going to pop up. I don't want to jump straight to the answer; it's better if we go through each of the steps one-by-one. That said, if you just want to see the solution it's at the bottom.
Let's start whacking.
Take a look at the implementors for Sum. They look like this:
impl Sum<f32> for f32
impl Sum<f64> for f64
impl Sum<i8> for i8
impl Sum<i16> for i16
impl Sum<i32> for i32
...
And so on.
The general pattern is obvious:
impl Sum<T> for T
None of them match your code because it's apparently trying to use Sum<&T>. We need to get rid of the references so Sum<&T> can be Sum<T>.
Where are the references coming from? iter() iterates over item references. It does this to avoid modifying the collection or its items.
We could use into_iter() to iterate over the items directly. Let's not, though. into_iter() converts a collection into an iterator. Converts... as in destroys. It moves all the items and consumes the collection in the process. Yikes!
Instead, let's copy the items with copied():
This is useful when you have an iterator over
&T, but you need an iterator overT.
let sum: f32 = self.iter().copied().sum();
We'll also need a T: Copy constraint:
impl<T> CentralMoment for [T]
where
    T: Copy,
This changes the error to:
error[E0277]: the trait bound `f32: Sum<T>` is not satisfied
  --> src/lib.rs:13:45
   |
13 |         let sum: f32 = self.iter().copied().sum();
   |                                             ^^^ the trait `Sum<T>` is not implemented for `f32`
Playground
TWhen you sum up a bunch of i32s you're going to get an i32. When you sum a bunch of f64s you're going to get an f64. This code says the result of sum() will be an f32, but really to be properly generic you should call it a T.
let sum: T = self.iter().copied().sum();
Now the error is:
error[E0277]: the trait bound `T: Sum` is not satisfied
  --> src/lib.rs:13:43
   |
13 |         let sum: T = self.iter().copied().sum();
   |                                           ^^^ the trait `Sum` is not implemented for `T`
T must be summableWe can fix that requiring that T: Sum:
where
    T: Copy + Sum,
Now we get:
error[E0369]: cannot divide `T` by `f32`
  --> src/lib.rs:16:13
   |
16 |         sum / self.len() as f32
   |         --- ^ ----------------- f32
   |         |
   |         T
Playground
T must be divisible by f32We're close, right? Let's require that T be divisible by f32s:
where
    T: Copy + Sum + Div<f32>,
Are you happy, compiler?
error[E0308]: mismatched types
  --> src/lib.rs:17:9
   |
15 |     fn mean(&self) -> f32 {
   |                       --- expected `f32` because of return type
16 |         let sum: T = self.iter().copied().sum();
17 |         sum / self.len() as f32
   |         ^^^^^^^^^^^^^^^^^^^^^^^ expected `f32`, found associated type
   |
   = note:         expected type `f32`
           found associated type `<T as Div<f32>>::Output`
   = help: consider constraining the associated type `<T as Div<f32>>::Output` to `f32`
Playground
No, of course not. Fine, fine, now what?
f32Well, we've required that T be divisible by f32. We haven't told it what type the result would be, though. It's not a given that operand types and result types are the same. They could be different. Rust is oh-so-flexible.
We need to put a constraint on the ::Output of the operation. Let's require that the output also be an f32:
where
    T: Copy + Sum + Div<f32, Output = f32>,
Result:
Compiling playground v0.0.1 (/playground)
 Finished dev [unoptimized + debuginfo] target(s) in 0.91s
Hallelujah! The princess is not in another castle.
Playground
use std::iter::Sum;
use std::ops::Div;
pub trait CentralMoment<Output = f32>
where
    Output: Copy,
{
    fn mean(&self) -> Output;
}
impl<T> CentralMoment for [T]
where
    T: Copy + Sum + Div<f32, Output = f32>,
{
    fn mean(&self) -> f32 {
        let sum: T = self.iter().copied().sum();
        sum / self.len() as f32
    }
}
Playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With