How to define sum over vectors (or iterators) in a

2019-05-06 21:37发布

I have the following following sum function for vectors of i32 that compiles and works fine:

fn sum_vec(s: &Vec<i64>, init: &i64) -> i64 {
    (*s).iter().fold(*init, |acc, &item| acc + item)
}

To teach myself about Rust generics, I would like to define the corresponding generic function, for any type T that implements addition of the form add(T, T) -> T. I tried

use std::ops::Add;

fn sum_gen_1<T: Add>(s: &Vec<T>, init: &T) -> T {
    (*s).iter().fold(*init, |acc, &item| acc + item)
}

But I get the following error

error[E0308]: mismatched types
 --> src/lib.rs:4:42
  |
4 |     (*s).iter().fold(*init, |acc, &item| acc + item)
  |                                          ^^^^^^^^^^ expected type parameter, found associated type
  |
  = note: expected type `T`
             found type `<T as std::ops::Add>::Output`

It seems that one has to further constrain the implementation of the Add trait so that the type Add::Output equals T. Following some documentation found on the interwebz (probably for an old version of Rust), I tried changing the type constraint as to T: Add<T, T>, i.e. declare the generic function as:

fn sum_gen_1<T: Add<T, T>>(s: &Vec<T>, init: &T) -> T

with the same function body as before. This time I got the error

error[E0107]: wrong number of type arguments: expected at most 1, found 2
 --> src/lib.rs:3:17
  |
3 | fn sum_gen_1<T: Add<T, T>>(s: &Vec<T>, init: &T) -> T {
  |                 ^^^^^^^^^ expected at most 1 type argument

What is the proper way to accomplish this? Should I use a different trait instead of Add? Maybe define my own trait and implement it for the types I want my sum to work for?

I noticed that the is a trait std::iter::AdditiveIterator which would seem to make my implementation unnecessary. However, it is marked as unstable and any attempt to use it would cause a compile error when using rustc-1.0.0-beta.

2条回答
We Are One
2楼-- · 2019-05-06 22:16

You almost got it. Associated types must be given by name/keyword, so you're looking for Add<T, Output = T>.

With that change, you face the problem that you liberally copy numbers around, but there is no Copy bound. I'd suggest the following implementation:

fn sum_vec<T>(s: &[T], init: &T) -> T
where
    T: Copy + Add<T, Output = T>,
{
    s.iter().fold(*init, |acc, &item| acc + item)
}

Changing &Vec<T> to &[T] has no effect, but it makes the function more general and loses nothing.

See also:

查看更多
叼着烟拽天下
3楼-- · 2019-05-06 22:22

After seeing the answer for vectors, I went on to implement essentially the same function for a general iterator of T:

use std::ops::Add;

fn sum_iter<I>(s: I, init: &I::Item) -> I::Item
where
    I: Iterator + Clone,
    <I as Iterator>::Item: Add<I::Item, Output = I::Item> + Copy,
{
    s.clone().fold(*init, |acc, item| acc + item)
}

It seems a bit verbose to have to type I::Item or <I as Iterator>::Item in three places... I've asked about that in Simplify where clause with repeated associated type restrictions

查看更多
登录 后发表回答