关于泛型:如何在 Rust 中编写一个函数来接受其 Item 满足某个特征的任何迭代器?

How do I write a function in Rust that accepts any iterator whose Item fulfills a trait?

这是我试图实现的人为示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
trait Double {
    fn get(&self) -> i32;
}

impl Double for i32 {
    fn get(&self) -> i32 { self * 2 }
}

fn foo<'a, I: Iterator<Item = &'a Double>>(is: I) {
    for i in is {
        println!("{}", i.get());
    }
}

fn main() {
    let is = vec![1, 2, 3, 4];
    foo(is.into_iter());
}

这里的错误是"预期的整数变量,找到 &Double"。

我在谷歌上搜索时遇到了麻烦,因为到处都在谈论迭代器作为特征。我正在尝试做的事情可能吗?


绑定的 Iterator<Item = &'a Double>> 表示你想要一个迭代器,它产生完全类型 &Double 的项目,它表示特征 Double 的特征对象。但是你想要一个迭代器,它产生任何实现特征 Double 的类型。这听起来非常相似,因此令人困惑,但这完全是关于动态与静态调度。你应该阅读 Rust 书中关于 trait 对象的章节,以了解到底发生了什么。

但是一个简单的总结:写作之间是有区别的

1
fn foo<T: MyTrait>(t: &T) {}

1
fn foo(t: &MyTrait) {}

你写的代码相当于后者,但实际上想要前者。

那么你如何在代码中表达你的意图?一种可能是引入另一个类型参数!

1
2
3
4
5
6
7
8
fn foo<'a, T, I>(is: I)
    where T: Double,
          I: Iterator<Item = &'a T>,
{
    for i in is {
        println!("{}", i.get());
    }
}

但您也可以只绑定迭代器的关联类型(参见 Francis Gagné\\'s answer):

1
2
3
4
fn foo(is: I)
    where I: Iterator,
          I::Item: Double,
{ ... }

然而,这两个版本略有不同,一个版本接受对实现 Double 的类型的引用的迭代器,而另一个版本则直接对实现 Double 的类型进行迭代。只使用最适合你的东西,或者用 AsRef.

之类的特征概括这两个东西


是的,这是可能的。您需要使用 where 子句来指定关联类型 I::Item.

的边界

1
2
3
4
5
6
7
8
fn foo(is: I)
    where I: Iterator,
          I::Item: Double,
{
    for i in is {
        println!("{}", i.get());
    }
}

(我还将 I: Iterator 绑定移动到 where 子句以将所有边界保持在一起。)