我如何使用 Iterator 特性来构建通用 API

How do I use the Iterator trait to build generic APIs

我可能只见树木不见森林,但我想知道如何实际设计我的方法,使其不针对硬集合类型而是针对迭代器。考虑这个方法。

pub fn print_strings(strings: Vec<String>) {
    for val in strings.iter() {
        println!("{}", val);
    }
}

显然,如果我想将它与 HashSetHashMap 一起使用,这就不够了。

所以,我尝试了这个:

use std::collections::*;

fn main () {
    let strings = vec!("Foo", "Bar");

    let mut more_strings = HashMap::new();
    more_strings.insert("foo", "bar");
    more_strings.insert("bar", "foo");

    print_strings(&strings.iter());
    print_strings(&more_strings.values())
}

fn print_strings(strings: &Iterator<Item=&str>) {
    for val in strings {
        println!("{}", val);
    }
}

Playpen(也可以查看冗长的编译器错误)

http://is.gd/EYIK11

不幸的是,这似乎也不起作用。我错过了什么?

当您在 Vec<T> 上调用 .iter() 时,您会得到一个 Iterator<Item=&T>。因此,当您在 Vec<&str> 上调用 .iter() 时,您会得到 Iterator<Item=&&str>,而不是 Iterator<Item=&str>。你应该看看 Iterator.cloned() 方法,它应该有助于解决你的问题。

另外,请注意,为了遍历迭代器,您必须能够改变它(拥有迭代器或拥有对它的可变引用)。所以仅仅拥有一个不可变的引用是没有用的。我建议将迭代器值移动到 print_strings 而不是通过引用传递它。如果你想为此使用特征对象,你可以通过使用 Box 来实现,但将 print_strings 设为通用函数可能更容易。

playpen
所以第一件事是你期待 Iterator<Item=&str>,但实际上是 Iterator<Item=&&str>
然后,您尝试调用 .iter(),但 Iterator 没有此方法。 您可以简单地删除 .iter() 调用和接收(并发送 ofc)&mut Iterator<...> 以获得 for 循环工作(for 循环需要一些东西,实现 IntoIterator&mut Iterator 就是那个东西)。
添加生命周期,一切就绪! :)
另外,我建议使用静态调度。您可以在我提供的示例中看到它。

更好的是,你可以做到

fn print_strings<Iterable>(strings: Iterable)
    where Iterable: IntoIterator,
          Iterable::Item: AsRef<str>
{
    for val in strings {
        println!("{}", val.as_ref());
    }
}

(感谢 Shepmaster 的改进。)

这意味着您可以使用 &mut Iterators 调用它进行动态调度 具体迭代器或集合类型进行静态调度。此外,迭代器类型可以是任何可以简单转换为&str的类型,包括但不限于&str&&str甚至String.

print_strings(&strings);
print_strings(strings.iter().map(|s| s.to_owned()));
print_strings(vec![&&&&"xyz"]);
print_strings(strings);
print_strings(more_strings.values());