我需要一个迭代器,该迭代器将流Read
实现的每一行的第一个字母单词。此迭代器:
[a-zA-Z]
以外的其他字符的第一个单词我最终完成了以下实现(test here):
fn get_first_words<'a>(r: &'a mut impl Read) -> impl Iterator<Item = Result<String>> + 'a {
BufReader::new(r).lines().filter_map(|rline| {
match rline.map(|line| {
line.split_whitespace()
.next()
.filter(|word| word.chars().all(char::is_alphabetic))
.map(&str::to_string)
}) {
Err(e) => Some(Err(e)),
Ok(Some(w)) => Some(Ok(w)),
Ok(None) => None,
}
})
}
这很好,但是比我预期的要复杂。此实现中有嵌套的迭代器,并且在对包含的值进行过滤时,为了保持Result
为包装类型,需要进行一些类型处理。
这可以用更少的嵌套逻辑和更少的类型变体来更简单地编写吗?
答案 0 :(得分:1)
您可以将match
表达式替换为Result::transpose()
。我还建议拆分返回第一个单词的函数,以使代码更具可读性。最后,您不需要接受&'a mut impl Read
–只需接受impl Read
也可以,因为有一个forwarding implementation为Read
实现了&mut impl Read
。在一起,简化的代码可能看起来像这样:
fn first_word(s: String) -> Option<String> {
s.split_whitespace()
.next()
.filter(|word| word.chars().all(char::is_alphabetic))
.map(From::from)
}
fn get_first_words(r: impl Read) -> impl Iterator<Item = Result<String>> {
BufReader::new(r)
.lines()
.filter_map(|line| line.map(first_word).transpose())
}
编辑:使用impl Read
而不是&mut impl Read
会导致可变引用被移到函数中,而不是隐式地重新借用,因此毕竟这不是一个好主意,因为这会使您感到困惑。记得在必要时明确地重新借用它们。