2017-02-07 15 views
8

Mục tiêu ban đầu của tôi là tìm nạp danh sách các từ, mỗi từ trên mỗi dòng và đặt chúng trong một HashSet, trong khi loại bỏ các dòng nhận xét và tăng lỗi I/O đúng cách. Căn cứ vào tập tin "stopwords.txt":Tại sao các chuỗi trong trình lặp của tôi lại được nối?

a 
# this is actually a comment 
of 
the 
this 

tôi quản lý để thực hiện các mã biên dịch như thế này:

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

Dưới đây là một playground đó cũng tạo ra các tập tin trên.

Tôi mong đợi có một bộ 4 chuỗi ở cuối chương trình. Trước sự ngạc nhiên của tôi, thực tế hàm trả về một tập chứa một chuỗi duy nhất với tất cả các từ nối:

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

Được dẫn dắt bởi một lời khuyên trong các tài liệu cho FromIterator, tôi đã thoát khỏi tất cả các cuộc gọi đến from_iter và sử dụng collect thay (Playground), đã thực sự giải quyết được vấn đề.

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

Tại sao các cuộc gọi trước để from_iter dẫn đến kết luận bất ngờ, trong khi collect() công trình cũng giống như dự định?

Trả lời

8

Một sinh sản đơn giản:

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

Vấn đề là ở đây, chúng tôi đang thu thập từ các iterator hai lần. Loại wordsResult<_, u8>. Tuy nhiên, Resultcũng thực hiện Iterator bản thân, vì vậy khi chúng ta gọi là from_iter trên là lúc kết thúc, trình biên dịch thấy rằng loại Ok phải String do chữ ký phương pháp. Làm việc ngược, bạn có thể xây dựng một String từ một trình lặp của Strings, vì vậy đó là những gì trình biên dịch chọn.

Loại bỏ các thứ hai from_iter sẽ giải quyết nó:

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

Hoặc cho bạn bản gốc:

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

Tất nhiên, tôi muốn khuyên bạn sử dụng thường collect thay vào đó, như tôi thích chaining:

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

Chết tiệt, có khoảng 2/3 của con đường thông qua viết của tôi lên. –

+1

@DK. có lẽ bạn đã có một lời giải thích tốt hơn/khác nhau/dễ hiểu hơn? – Shepmaster

+0

Không, nó đã được nhiều hơn hoặc ít hơn cùng một điều, được viết theo thứ tự ngược lại. –

Các vấn đề liên quan