Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
722 views
in Technique[技术] by (71.8m points)

rust - Cannot create a generic function that uses a literal zero

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 is why I'm trying to make this function: extern crate num;

use num::Integer;

fn positions<T: Integer>(start: T, step: T, len: T) -> Vec<T> {
    (0..len).map(|i| start + step * i).collect()
}

fn main() {
    println!("{:?}", positions(10, 2, 10));
}

Except I am getting a compiler error:

error[E0308]: mismatched types
 --> src/main.rs:6:9
  |
6 |     (0..len).map(|i| start + step * i).collect()
  |         ^^^ expected integral variable, found type parameter
  |
  = note: expected type `{integer}`
             found type `T`
  = help: here are some functions which might fulfill your needs:
          - .div_floor(...)
          - .gcd(...)
          - .lcm(...)
          - .mod_floor(...)

error[E0308]: mismatched types
 --> src/main.rs:6:37
  |
6 |     (0..len).map(|i| start + step * i).collect()
  |                                     ^ expected type parameter, found integral variable
  |
  = note: expected type `T`
             found type `{integer}`
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...