Reputation: 1183
In writing my own generic sigma summation function to get practice with Rust, I've hit an issue with providing a seed value of zero as the accumulator.
fn sigma<I, T, F>(iter: I, func: F) -> T
where I: Iterator<Item=T>,
T: Add<Output=T>,
F: Fn(T) -> T
{
iter.fold(0, |acc, x| acc + func(x))
}
I understand that it's wrong, as 0 is a concrete type, and thus not T
. Other answers (like this and this one) rely on constructs like Int::zero
, which are deprecated as of 1.11.
There are other ways of doing this, but I am particularly interested in how it should be done, as testing for one, zero, or negativity is a common operation in numeric procedures that I'll hit again soon enough. Plus, now I'm curious.
My Rust version is 1.16.
Upvotes: 1
Views: 146
Reputation: 30042
Asking for how your function should be done can become opinion-based: there are multiple good ways of achieving this. Nevertheless, it is answerable if we narrow down to these two approaches, which I would consider typical and idiomatic:
Zero
and One
were deprecated (never even stabilized, in fact!) from the standard library mostly because there is a more generalized means of making products and sums from iterators: the Sum
and Product
traits are relied upon by iterators when calling methods sum()
and product()
, and can even yield a result of a type other than the items'.
use std::iter::{Iterator, Sum};
fn sigma<I, T, F>(iter: I, func: F) -> T
where I: Iterator<Item = T>,
T: Sum,
F: Fn(T) -> T
{
iter.map(func).sum::<T>()
}
Playground. I took the liberty of moving the element transformation to a map
in the iterator definition chain, thus enabling the use of sum()
as a terminal operation.
The trait Zero
and One
are still available in crate num
(or num-traits
), so you can use that instead.
extern crate num;
use std::ops::Add;
use std::iter::Iterator;
use num::Zero;
fn sigma<I, T, F>(iter: I, func: F) -> T
where I: Iterator<Item = T>,
T: Zero + Add<Output = T>,
F: Fn(T) -> T
{
iter.map(func).fold(T::zero(), |a, b| a + b)
}
Upvotes: 4