为什么我的迭代器中的字符串被连接起来?

E_n*_*ate 8 iterator type-inference rust

我最初的目标是获取一个单词列表,每行一个HashSet,并将它们放入a中,同时丢弃注释行并正确地引发I/O错误.给定文件"stopwords.txt":

a
# this is actually a comment
of
the
this
Run Code Online (Sandbox Code Playgroud)

我设法使代码编译如下:

fn stopword_set() -> io::Result<HashSet<String>> {
    let words = Result::from_iter(
        BufReader::new(File::open("stopwords.txt")?)
                .lines()
                .filter(|r| match r {
                    &Ok(ref l) => !l.starts_with('#'),
                    _ => true
                }));
    Ok(HashSet::from_iter(words))
}

fn main() {
    let set = stopword_set().unwrap();
    println!("{:?}", set);
    assert_eq!(set.len(), 4);
}
Run Code Online (Sandbox Code Playgroud)

这是一个也在上面创建文件的游乐场.

我期望在程序结束时有一组4个字符串.令我惊讶的是,该函数实际上返回一个包含单个字符串的集合,其中所有单词都连接在一起:

{"aofthethis"}
thread 'main' panicked at 'assertion failed: `(left == right)` (left: `1`, right: `4`)'
Run Code Online (Sandbox Code Playgroud)

在文档中的一条建议的带领下FromIterator,我摆脱了所有的调用from_iter和使用collect(Playground),这确实解决了问题.

fn stopword_set() -> io::Result<HashSet<String>> {
    BufReader::new(File::open("stopwords.txt")?)
            .lines()
            .filter(|r| match r {
                &Ok(ref l) => !l.starts_with('#'),
                _ => true
            }).collect()
}
Run Code Online (Sandbox Code Playgroud)

为什么先前的调用from_iter导致意外的推断,同时collect()按预期工作?

She*_*ter 8

更简单的复制:

use std::collections::HashSet;
use std::iter::FromIterator;

fn stopword_set() -> Result<HashSet<String>, u8> {
    let input: Vec<Result<_, u8>> = vec![Ok("foo".to_string()), Ok("bar".to_string())];
    let words = Result::from_iter(input.into_iter());
    Ok(HashSet::from_iter(words))
}

fn main() {
    let set = stopword_set().unwrap();
    println!("{:?}", set);
    assert_eq!(set.len(), 2);
}
Run Code Online (Sandbox Code Playgroud)

问题是,在这里,我们从迭代器中收集两次.类型wordsResult<_, u8>.但是,Result 实现了Iterator自己,所以当我们最后调用from_iter它时,编译器会发现Ok类型必须是String由方法签名引起的.向后工作,你可以String从迭代器构造一个Strings,这就是编译器选择的东西.

删除第二个from_iter将解决它:

fn stopword_set() -> Result<HashSet<String>, u8> {
    let input: Vec<Result<_, u8>> = vec![Ok("foo".to_string()), Ok("bar".to_string())];
    Result::from_iter(input.into_iter())
}
Run Code Online (Sandbox Code Playgroud)

或者你原来的:

fn stopword_set() -> io::Result<HashSet<String>> {
    Result::from_iter(
        BufReader::new(File::open("stopwords.txt")?)
                .lines()
                .filter(|r| match r {
                    &Ok(ref l) => !l.starts_with('#'),
                    _ => true
                }))
}
Run Code Online (Sandbox Code Playgroud)

当然,我通常建议使用collect,因为我更喜欢链接:

fn stopword_set() -> io::Result<HashSet<String>> {
    BufReader::new(File::open("stopwords.txt")?)
        .lines()
        .filter(|r| match r {
            &Ok(ref l) => !l.starts_with('#'),
            _ => true,
        })
        .collect()
}
Run Code Online (Sandbox Code Playgroud)

  • 该死的,通过写我的约2/3. (2认同)