Need help understanding arithmetic using generic types

I'm getting confused with generic arithmetic and was wondering if someone could help explain what I'm doing wrong and why.

The following generic trait implementation works just fine as intended:

pub trait CentralMoment<Output = f64>
where
    Output: Copy,
{
    fn var(&self) -> Output;
    fn mean(&self) -> Output;
}

impl<T: Copy + Into<f64>> CentralMoment for [T]
where
    T: Copy + Into<f64>,
{
    fn var(&self) -> f64 {
        let mean: f64 = self.mean(); 
        let mut ss = 0f64;
        for i in 0..self.len() {
            let sq_diff = (self[i].into() - mean).powf(2.);
            ss += sq_diff;
        }
        ss / (self.len() as f64)
    }
    fn mean(&self) -> Output { // calculate mean }
}

But then I tried changing the implementation of fn var(&self) -> f64 so that instead of using a ranged-based loop, I use a for-each loop instead, like so:

    fn var(&self) -> f64 {
        let mean: f64 = self.mean();
        let mut ss = 0f64;
        // use for-each style loop here instead
        for n in self {
            let sq_diff = (*n - mean).powf(2.);
            ss += sq_diff;
        }
        ss / (self.len() as f64)
    }

But the above code now only works for Vec<{float}> types and not Vec<{integer}> types.
So, this works fine:

let nums = vec![5., 5., 10., 3.];
let var = nums.var();
assert_eq!(var, 6.6875)

But now I get the following compile error when I try to invoke fn var(&self) -> f64 on a vec![5, 5, 10, 3].

error[E0599]: the method `var` exists for struct `Vec<{integer}>`, but its trait bounds were not satisfied
   --> src/lib.rs:185:24
    |
185 |         let var = nums.var();
    |                        ^^^ method cannot be called on `Vec<{integer}>` due to unsatisfied trait bounds
    |
    = note: the following trait bounds were not satisfied:
            `<{integer} as Sub<f64>>::Output = f64`
            which is required by `[{integer}]: CentralMoment`
            `{integer}: Sub<f64>`
            which is required by `[{integer}]: CentralMoment`

I tried adding the trait bound to T: std::ops::Sub<f64, Output = f64>, but that still didn't work. Can someone please explain where my error lies? It feels like I'm missing a key piece of information re: generics and traits that I need to go over and review.

Thank you in advance. And kindly let me know if you need any further clarification or information.

The implementation in your first code block contains self[i].into(), enabling conversion from T to f64. The second one does not have that .into(). (There must also be a difference in the trait bounds, but you didn't show that.)

for n in self {
            let sq_diff = ((*n).into() - mean).powf(2.);

would get you the same as your original code, given that you also have the same bounds on the impl.

Rust's standard library does not provide arithmetic (Sub) that implicitly converts, only explicit conversions. The only standard type that implements Sub<f64> is f64 itself. So, that bound cannot be satisfied by anything but f64. Your initial code worked because it asked for conversion to f64.

2 Likes

OK. That explains alot. Thank you!

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.