I'm trying to write a generic function that returns an iterator.
fn one_to_ten<T: Add>() -> std::iter::Iterator<Item = T> {
(1..5).chain(5..=10)
}
It's generic because it needs to work for u8
through u128
, and preferably also i8
through i128
, for the following code:
let mut sum: u64 = 0;
for x in one_to_ten() {
sum += x;
}
Unfortunately, Rust complains:
= help: the trait
std::marker::Sized
is not implemented for(dyn std::iter::Iterator<Item = T> + 'static)
error[E0277]: the size for values of typedyn std::iter::Iterator<Item = _>
cannot be known at compilation time
and
error[E0308]: mismatched types
expected trait std::iter::Iterator, found structstd::iter::Chain
I'm out of my depth here. Rust seems to think I'm trying to implement a dynamically dispatched function, when I'm trying to implement a generic one instead. (And, for some reason, my chained iterator isn't accepted as an iterator – my guess is that I'm specifying a type instead of a trait, but std::iter::Iterator
is a trait, not a type.)
What should my code look like?
Traits are unsized so you cannot return a value of trait type. Instead you must return a value of a type that implements that trait:
fn one_to_ten<T: Add>() -> impl std::iter::Iterator<Item = T> {
(1..5).chain(5..=10)
}
Unfortunately that brings another issue, that the (1..5)
code builds a range of integers, not of T
. You can fix that with a custom trait, as the std::iter::Step
is still unstable.
Something like this (playground):
pub trait MyRange: Sized {
type Iter: std::iter::Iterator<Item = Self>;
fn new(a: i32, b: i32) -> Self::Iter;
fn new_incl(a: i32, b: i32) -> Self::Iter {
Self::new(a, b + 1)
}
}
impl MyRange for i8 {
type Iter = std::ops::Range<Self>;
fn new(a: i32, b: i32) -> Self::Iter {
(a as i8 .. b as i8) //check overflow?
}
}
pub fn one_to_ten<T: Add + MyRange>() -> impl std::iter::Iterator<Item = T> {
T::new(1, 5).chain(T::new_incl(5, 10))
}
And then implement the MyRange
trait for every integer type you need.