【问题标题】:Why are the strings in my iterator being concatenated?为什么我的迭代器中的字符串被连接起来?
【发布时间】:2017-06-25 10:19:45
【问题描述】:

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

a
# this is actually a comment
of
the
this

我设法使代码像这样编译:

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);
}

这是一个playground,它也创建了上面的文件。

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

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

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()
}

为什么之前对 from_iter 的调用会导致意外的推断,而 collect() 的工作原理却是预期的?

【问题讨论】:

    标签: iterator rust type-inference


    【解决方案1】:

    更简单的复制:

    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);
    }
    

    问题是在这里,我们从迭代器中收集了两次。 words 的类型是 Result&lt;_, u8&gt;。然而,Result 实现了Iterator本身,所以当我们最后调用from_iter时,编译器看到Ok类型必须是String,因为方法签名。向后工作,您可以从 Strings 的迭代器构造 String,这就是编译器选择的内容。

    删除第二个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())
    }
    

    或者您的原件:

    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
                    }))
    }
    

    当然,我通常建议使用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()
    }
    

    【讨论】:

    • 该死,我的写了大约 2/3。
    • @DK。也许你有更好/不同/更容易理解的解释?
    • 不,它或多或少是相同的东西,以相反的顺序编写。
    猜你喜欢
    • 2021-12-17
    • 2012-08-07
    • 1970-01-01
    • 2014-11-23
    • 2020-11-07
    • 1970-01-01
    • 2013-01-26
    • 2016-08-22
    • 1970-01-01
    相关资源
    最近更新 更多