返回迭代器(或任何其他特性)的正确方法是什么?

下面的 Rust 代码编译和运行时没有任何问题。

fn main() {
let text = "abc";
println!("{}", text.split(' ').take(2).count());
}

在那之后,我尝试了这样的东西... ... 但它不能编译

fn main() {
let text = "word1 word2 word3";
println!("{}", to_words(text).take(2).count());
}


fn to_words(text: &str) -> &Iterator<Item = &str> {
&(text.split(' '))
}

主要问题是我不确定函数 to_words()应该有什么返回类型,编译器说:

error[E0599]: no method named `count` found for type `std::iter::Take<std::iter::Iterator<Item=&str>>` in the current scope
--> src/main.rs:3:43
|
3 |     println!("{}", to_words(text).take(2).count());
|                                           ^^^^^
|
= note: the method `count` exists but the following trait bounds were not satisfied:
`std::iter::Iterator<Item=&str> : std::marker::Sized`
`std::iter::Take<std::iter::Iterator<Item=&str>> : std::iter::Iterator`

运行这个程序的正确代码是什么? ... ... 我的知识差距在哪里?

55364 次浏览

我发现让编译器指导我是很有用的:

fn to_words(text: &str) { // Note no return type
text.split(' ')
}

汇编结果如下:

error[E0308]: mismatched types
--> src/lib.rs:5:5
|
5 |     text.split(' ')
|     ^^^^^^^^^^^^^^^ expected (), found struct `std::str::Split`
|
= note: expected type `()`
found type `std::str::Split<'_, char>`
help: try adding a semicolon
|
5 |     text.split(' ');
|                    ^
help: try adding a return type
|
3 | fn to_words(text: &str) -> std::str::Split<'_, char> {
|                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

遵循编译器的建议,复制粘贴,作为我的返回类型(稍微清理一下) :

use std::str;


fn to_words(text: &str) -> str::Split<'_, char> {
text.split(' ')
}

问题是您不能返回像 Iterator这样的 trait,因为 trait 没有大小。这意味着 Rust 不知道为类型分配多少空间。你 也不能返回对局部变量的引用,所以返回 &dyn Iterator是不可能的。

隐性特征

从 Rust 1.26开始,您可以使用 impl trait:

fn to_words<'a>(text: &'a str) -> impl Iterator<Item = &'a str> {
text.split(' ')
}


fn main() {
let text = "word1 word2 word3";
println!("{}", to_words(text).take(2).count());
}

对于如何使用它有一些限制。您只能返回一个类型(不能有条件!)它必须用在一个自由函数或固有的实现上。

装箱

如果你不介意失去一点效率,你可以返回一个 Box<dyn Iterator>:

fn to_words<'a>(text: &'a str) -> Box<dyn Iterator<Item = &'a str> + 'a> {
Box::new(text.split(' '))
}


fn main() {
let text = "word1 word2 word3";
println!("{}", to_words(text).take(2).count());
}

这是考虑到 克劳斯·福尔曼的主要选项。也就是说,代码的确切实现是在运行时而不是在编译时决定的。这意味着这适用于需要根据条件返回多个具体类型的迭代器的情况。

新型的

use std::str;


struct Wrapper<'a>(str::Split<'a, char>);


impl<'a> Iterator for Wrapper<'a> {
type Item = &'a str;


fn next(&mut self) -> Option<&'a str> {
self.0.next()
}


fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}


fn to_words(text: &str) -> Wrapper<'_> {
Wrapper(text.split(' '))
}


fn main() {
let text = "word1 word2 word3";
println!("{}", to_words(text).take(2).count());
}

类型别名

作为 被 Reem 指出

use std::str;


type MyIter<'a> = str::Split<'a, char>;


fn to_words(text: &str) -> MyIter<'_> {
text.split(' ')
}


fn main() {
let text = "word1 word2 word3";
println!("{}", to_words(text).take(2).count());
}

处理关闭

impl Trait不能使用时,闭包会使事情变得更加复杂。闭包创建匿名类型,这些类型不能在返回类型中命名:

fn odd_numbers() -> () {
(0..100).filter(|&v| v % 2 != 0)
}
found type `std::iter::Filter<std::ops::Range<{integer}>, [closure@src/lib.rs:4:21: 4:36]>`

在某些情况下,这些闭包可以替换为函数,这些函数可以命名为:

fn odd_numbers() -> () {
fn f(&v: &i32) -> bool {
v % 2 != 0
}
(0..100).filter(f as fn(v: &i32) -> bool)
}
found type `std::iter::Filter<std::ops::Range<i32>, for<'r> fn(&'r i32) -> bool>`

以下是上述建议:

use std::{iter::Filter, ops::Range};


type Odds = Filter<Range<i32>, fn(&i32) -> bool>;


fn odd_numbers() -> Odds {
fn f(&v: &i32) -> bool {
v % 2 != 0
}
(0..100).filter(f as fn(v: &i32) -> bool)
}

处理条件句

如果需要有条件地选择迭代器,请参考 有条件地遍历几个可能的迭代器之一

添加到@Shepmaster 回答。如果你需要闭包和闭包捕获一些参数,你可以使用这个例子

fn find_endpoint<'r>(
descriptors: &'r mut rusb::EndpointDescriptors<'r>,
direction: rusb::Direction,
) -> FilterMap<
&mut rusb::EndpointDescriptors<'_>,
impl FnMut(rusb::EndpointDescriptor<'r>) -> Option<u8>,
> {
descriptors.filter_map(move |ep| {
if ep.direction() == direction {
Some(ep.address())
} else {
None
}
})
}