我如何告诉Rust某些东西实现了特征?

时间:2019-06-22 11:31:54

标签: rust annotations traits generic-programming

我正在尝试编写一个返回迭代器的泛型函数。

fn one_to_ten<T: Add>() -> std::iter::Iterator<Item = T> {
    (1..5).chain(5..=10)
}

之所以通用,是因为它需要为u8u128工作,并且最好为i8i128工作,适用于以下代码:

let mut sum: u64 = 0;
for x in one_to_ten() {
    sum += x;
}

不幸的是,Rust抱怨:

  

=帮助:std::marker::Sized未实现特征(dyn std::iter::Iterator<Item = T> + 'static)
  错误[E0277]:在编译时无法知道类型dyn std::iter::Iterator<Item = _>的值的大小

  

错误[E0308]:类型不匹配
  预期特征std :: iter :: Iterator,找到结构std::iter::Chain

我不在这里。当我尝试实现一个通用的函数时,Rust似乎认为我正在尝试实现一个动态调度的函数。 (并且由于某种原因,我的链式迭代器不被接受为迭代器–我的猜测是我指定的是类型而不是特征,但是std::iter::Iterator是特征,而不是类型。)

我的代码应该是什么样的?

1 个答案:

答案 0 :(得分:3)

特征没有大小,因此您不能返回特征类型的值。相反,您必须返回实现该特征的类型的值:

fn one_to_ten<T: Add>() -> impl std::iter::Iterator<Item = T> {
    (1..5).chain(5..=10)
}

不幸的是,这带来了另一个问题,(1..5)代码生成的是整数范围,而不是T的整数范围。您可以使用自定义特征来解决此问题,因为std::iter::Step仍然不稳定。

类似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))
}

然后为您需要的每种整数类型实现MyRange特性。