Traits and folding a Vec<T>


#1

Hi,

I am trying to write a generic vector and does a bunch of computation. In one step, I need to fold/reduce the values and this has proved much harder (and frustrating) than I thought. I am using rust 1.2.

Here is a simplified version,

extern crate num;
use std::ops::Add;

pub fn foo<T: Add + num::traits::Num >(a: Vec<T>) -> T {
    let b = vec![1,2,3,5,7,8];
    let z = b.iter().fold(0, |s,v| s+v);
    let res = a.iter().fold(num::zero(), |sum, x| sum + x);
    res
}

I get the following error,

src\utils.rs:15:51: 15:58 error: the trait `core::ops::Add<&T>` is not implement
ed for the type `T` [E0277]
src\utils.rs:15     let res = a.iter().fold(num::zero(), |sum, x| sum + x);
                                                                  ^~~~~~~
note: in expansion of closure expansion

As a related question, why is there not a simple Numeric trait that provides basic numeric properties. I was unable to find one. In other words why do I need to work at the granularity of specifying Add, Sub etc …


#2

This is what works for me on playpen (using std::num instead of num since that’s an external crate):

#![feature(zero_one)]
use std::ops::Add;
use std::num::Zero;

pub fn foo<T: Copy + Add<Output=T> + Zero>(a: Vec<T>) -> T {
    let b = vec![1,2,3,5,7,8];
    let z = b.iter().fold(0, |s,v| s+v);
    let res = a.iter().fold(Zero::zero(), |sum, x| sum + *x);
    res
}

fn main() {
    foo(vec![1,2,3,4]);
}

BTW, you can replace let res = xxx; res by xxx :smile:


#3

I get errors with that code

error: use of unstable library feature 'zero_one': unsure of placement, wants to use associated constants. 

I would think that what I am trying to do would be fairly standard. Is there a simpler way to achieve this? Using a fold and not with a loop preferably.

The use of let res = xxx; was only for debugging and to make it as similar to the preceding statement as possible. BTW, why is there a difference with the simple example? as in why *x and not x?

Thanks.


#4

Did you try this (untested):

extern crate num;
use std::ops::Add;

pub fn foo<T: Add + num::traits::Num >(a: Vec<T>) -> T {
    let b = vec![1,2,3,5,7,8];
    let z = b.iter().fold(0, |s,v| s+v);
    let res = a.iter().fold(num::zero(), |sum, &x| sum + x);
    //                                         ^
    res
}

Note: & is effectively the same as @birkenfeld suggested. Regarding why dereferencing: see these two analysis examples.


#5

You should be fine using num's trait instead. I just can’t use that on playpen. But as you saw, the standard traits are still unstable, so feature(...) only works on nightly Rust.

*x is used to “make” a T out of &T, since the iterator yields &Ts but the Add impl selected is for T + T -> T.

It is possible to do this with the T + &T -> T impl. The shortest generic spec I found for that is

pub fn foo<T: Copy + for<'r> Add<&'r T, Output=T> + Zero>(a: Vec<T>) -> T {
    a.iter().fold(Zero::zero(), |sum, x| sum + x)
}

but it might be easier; I’ll let the experts take over for that :smiley:


I don't understand explicit lifetime parameters
#6

Thanks for the replies. Here is the version that finally worked …

extern crate num;
use std::ops::Add;

pub fn foo<T: Copy + Add<Output=T> + num::traits::Num>(a: Vec<T>) -> T {
    a.iter().fold(num::zero(), |sum, x| sum + *x)
}

#7

Great you got it to work. Don’t be discouraged, Rust’s generics system can be hard to start out with, since you have to specify quite a lot of bounds and it can get verbose. where clauses can make it more readable.

However, I just had a look at the Num trait and it already includes Add<Output=Self> (and Sub, Div, …) so a) you can leave out the Add bound in your code and b) it answers the question from the first post (“why is there not a simple Numeric trait that provides basic numeric properties”) :smile: