2017-02-07 6 views
8

私の本来の目標は、コメント行を破棄してI/Oエラーを適切に発生させながら、各行に1つの単語のリストを取り出してHashSetに入れることです。私は、コードを作るために管理イテレータの文字列が連結されているのはなぜですか?

a 
# this is actually a comment 
of 
the 
this 

は次のようにコンパイルします:ファイル「stopwords.txt」を考える

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つの文字列のセットがあると思います。驚いたことに、関数が実際に連結すべての単語を備えた単一の文字列が含まれるセットを返します:FromIteratorためのドキュメントでアドバイス断片が率いる

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

を、私はfrom_iterにすべてのコールを取り除き、代わりにcollectを使用しました(Playground)、実際問題を解決しました。 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() 
} 

はなぜ、予期しない推論につながるfrom_iterへの以前の呼び出しですか?

答えて

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

ここでの問題は、私たちは二度イテレータから収集しているということです。 wordsのタイプはResult<_, u8>です。しかし、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

くそー、書き込むことで2/3の方法を手に入れました。 –

+1

@DK。多分あなたはより良い/異なった/より理解できる説明を持っていますか? – Shepmaster

+0

ナー、それは多かれ少なかれ逆の順序で書かれていました。 –

関連する問題