我如何使用 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);
}
}
显然,如果我想将它与 HashSet
或 HashMap
一起使用,这就不够了。
所以,我尝试了这个:
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(也可以查看冗长的编译器错误)
不幸的是,这似乎也不起作用。我错过了什么?
当您在 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 Iterator
s 调用它进行动态调度 或 具体迭代器或集合类型进行静态调度。此外,迭代器类型可以是任何可以简单转换为&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());
我可能只见树木不见森林,但我想知道如何实际设计我的方法,使其不针对硬集合类型而是针对迭代器。考虑这个方法。
pub fn print_strings(strings: Vec<String>) {
for val in strings.iter() {
println!("{}", val);
}
}
显然,如果我想将它与 HashSet
或 HashMap
一起使用,这就不够了。
所以,我尝试了这个:
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(也可以查看冗长的编译器错误)
不幸的是,这似乎也不起作用。我错过了什么?
当您在 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 Iterator
s 调用它进行动态调度 或 具体迭代器或集合类型进行静态调度。此外,迭代器类型可以是任何可以简单转换为&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());