Jonatan Öström
Jonatan Öström

Reputation: 2609

Sum array along dimension

In many languages (Fortran, Matlab/Octave, Julia, etc) an expression like sum(array,n) will sum the values of an array along the n:th dimension and outputting an array of one lower dimensionality. Is there something equivalent in Rust?

I tried:

fn main() {
    let arr1: [f64; 5] = [1.1, 1.2, 2.3, 3.4, 4.5555];
    println!("this {}", arr1.iter().sum())
}

with this error:

error[E0282]: unable to infer enough type information about `_`
 --> src/main.rs:3:37
  |
3 |     println!("this {}", arr1.iter().sum())
  |                                     ^^^ cannot infer type for `_`
<std macros>:2:27: 2:58 note: in this expansion of format_args!
<std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>)
src/main.rs:3:5: 3:43 note: in this expansion of println! (defined in <std macros>)
  |
  = note: type annotations or generic parameter binding required

Upvotes: 0

Views: 647

Answers (2)

E_net4
E_net4

Reputation: 29983

The "unable to infer enough type information about _" error message is explained in a few other questions. See Error: unable to infer enough type information about `_`; type annotations or generic parameter binding required and Unable to infer enough type information about _; type annotations or generic parameter binding required. Basically, it means that the compiler doesn't have enough information to specify all type parameters in a function or a data type.

Nevertheless, this particular case may arouse some confusion: why can't Iterator.sum() just infer the resulting sum's type to the iterator's Item? By supposedly adding up f64s, we'd be expecting f64 as an outcome, right? Well, the method sum is actually defined like this:

fn sum<S>(self) -> S 
    where S: Sum<Self::Item>

Where S implements another kind of sum function (see trait Sum) that takes an iterator:

pub trait Sum<A = Self> {
    fn sum<I>(iter: I) -> Self where I: Iterator<Item=A>;
}

This trait gives us the freedom to sum numbers and references to numbers alike:

static MAGIC_CODE: u32 = 0xDEADBEEF;
static BLAND_CODE: u32 = 0x1234ABCD;

fn main() {

    let sum1: u32 = vec![MAGIC_CODE, BLAND_CODE] // vec! infers to Vec<u32>
        .into_iter().sum();

    let sum2 = vec![&MAGIC_CODE, &BLAND_CODE] // vec! infers to Vec<&u32>
        .into_iter().sum::<u32>();


    assert_eq!(sum1, sum2);
}

On the other hand, this also means that the definition of a sum becomes more loose: any other data type implementing Sum<u32> (or Sum<&u32>) could take its place in the code above, which leads to the aforementioned ambiguity. For the purpose of demonstration, this code also compiles:

use std::iter::Sum;

struct Accumulator(bool);

impl Sum<u32> for Accumulator {
    fn sum<I: Iterator<Item = u32>>(mut iter: I) -> Self {
        Accumulator(iter.any(|v| v != 0))
    }
}

fn main() {
    let sum3: Accumulator = {
        let data = vec![MAGIC_CODE, BLAND_CODE];

        data.into_iter().sum()
    };

    assert!(sum3.0);
}

Full code on Playground.

Upvotes: 1

Slava Semushin
Slava Semushin

Reputation: 15204

In this case you have to explicitly specify type of the elements:

println!("this {}", arr1.iter().sum::<f64>())

You were very close :)


Another option that were suggested by @E_net4 is to use separate binding:

let res: f64 = arr1.iter().sum();
println!("this {}", res)

Upvotes: 3

Related Questions