Cannot create a generic function that uses a literal zero

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.

Leave a Comment