I am trying to write a Rust function that is similar to the built-in Range
, but I want something that will return only X numbers, and return it as a list, which
The problem is 0
. I'm unclear on the exact rules right now, but let's be general: 0
is of some specific integer type, which may or may not be the same thing as whatever T
is. Thus, the compiler can't work out what the type parameter to range
is supposed to be.
You can resolve this by using Zero::zero:
fn positions<T: Integer>(start: T, step: T, len: T) -> Vec<T> {
(T::zero()..len).map(|i| start + step * i).collect()
}
This gives the compiler enough leeway to infer that the two arguments to range
are of the same type. However, that's still not enough to use Range
as an iterator:
error: no method named `map` found for type `std::ops::Range<T>` in the current scope
--> src/main.rs:8:22
|
8 | (T::zero()..len).map(|i| start + step * i).collect()
| ^^^
|
= note: the method `map` exists but the following trait bounds were not satisfied: `T : std::iter::Step`, `&'a T : std::ops::Add`, `std::ops::Range<T> : std::iter::Iterator`
Unfortunately, as of Rust 1.17, the Step trait is unstable, so there's currently no good way to solve this problem using stable Rust.
Using unstable Rust, you can require implementations of Step
:
#![feature(step_trait)]
extern crate num;
use num::Integer;
fn positions<T>(start: T, step: T, len: T) -> Vec<T>
where T: Integer + std::iter::Step + Copy,
for<'a> &'a T: std::ops::Add<Output = T>
{
(T::zero()..len).map(|i| start + step * i).collect()
}
fn main() {
println!("{:?}", positions(10, 2, 10));
}
You also need to require that T
can be copied (or cloned, if you like) because the implementation of Add
and Mul
consumes the operands by value, which would mean that start + step * i
could only be called once, except it needs to be called multiple times.