Using Iterator::sum in an expression


#1

This function

fn f() -> f64 {
   0.0f64 +  vec![1.0f64].into_iter().sum()
}

does not compile. The error is:

error: type annotations required: cannot resolve `<f64 as std::ops::Add<_>>::Output == f64` [E0284]
 --> <anon>:2:4
  |>
2 |>    0.0f64 +  vec![1.0f64].into_iter().sum()
  |>   

Adding a type parameter to sum solves the problem:

fn f() -> f64 {
   0.0f64 +  vec![1.0f64].into_iter().sum::<f64>()
}

Is this a bug or just a (bad) usability issue?


#2

I am not 100% sure; it certainly isn’t great usability-wise, but I’m not sure if it’s something that’s fixible or not, other than an improved diagnostic.


#3

The usability of sum() isn’t very good, even tiny things stop the type inferencer:

fn total1(a: &[u32]) -> u32 { // OK
    a.iter().sum()
}

fn total3(a: &[u32]) -> u32 { // Error
    a.iter().sum() + 5u32
}

fn main() {}

#4

I reported this bug.


#5
use std::iter::Sum;
use std::ops::Add;
 
#[derive(Debug)]
struct Stack<T> {
    data: Vec<T>,
    sum: u64
}

impl<T> Stack<T> {
    fn from_vec<'a>(vec: Vec<T>) -> Stack<T>
        where T: 'a + Add<&'a T, Output=u64>,
              u64: Sum<&'a T>
    {
        let sum = vec.iter().sum::<u64>();
        Stack { data: vec, sum: sum }
    }
}

fn main() {
    let stack = Stack::from_vec(vec![1u32, 2, 3]);
    println!("{:?}", stack);
}

I have a similar problem. I want to have a struct with a Vec<T> field and a field of type u64 which stores the sum of the elements in Vec<T>, I chose u64 to avoid overflow from summing T, however it seems I cannot have T types different from u64. What am I doing wrong?

Here’s a link to playground https://is.gd/7ZvvbB


#6

As far as I can tell, it’s the u64: Sum<&'a T> that restricts T to u64. Personally, I would write the from_vec function like this:

fn from_vec<'a>(vec: Vec<T>) -> Stack<T>
    where T: 'a + Into<u64> + Copy
{
    let sum = vec.iter().map(|&v| v.into()).sum();
    Stack { data: vec, sum: sum }
}

Instead of taking a T that can somehow be summed to an u64, just take a T that can be converted to u64 and sum those.


#7

Well, sure I can do that, but I thought sum::<T>() was meant to sum into T and that some kind of similar solution was enabled behind the seens. Besides that, isn’t Copy a performance penalty here (I’m not sure of Into<T>)?


#8

Well, in principle sum::<T>() does sum into T, as long as there exists a exist an impl of Sum<T> for the Item type of your iterator. The std library only provides impls of Sum where T and the item type are identical.

The compiler will generate a version of from_vec for each T you call it with and optimize the function body for that specific type. The (implicit) copy() and the into() will be inlined and compiled to just what is needed to sum those numbers. (At least in a release build.)
In fact, the primitive numbers all implement Copy and anytime you do any calculation you are implicitly using it. Without it, the Rust ownership model wouldn’t even allow this:

let factor = 4.2;
let a = 33.0 * factor;
let b = 44.0 * factor;

Without Copy, the factor would have been moved into the 33.0 * factor expression and not available in the second expression.


#9

Thanks for clarification. I think it would be nice to have an impl of Sum<T> for types where this is more or less straightforward…