2016-11-01 17 views
5

Tôi đang cố gắng lặp qua các ký tự trong stdin. Phương pháp Read.chars() đạt được mục tiêu này, nhưng không ổn định. Cách thay thế rõ ràng là sử dụng Read.lines() với số flat_map để chuyển đổi nó thành trình lặp ký tự.Tại sao .flat_map() với .chars() không hoạt động với std :: io :: Lines, nhưng với vector của Strings?

Điều này có vẻ như nó sẽ hoạt động, nhưng không, dẫn đến lỗi borrowed value does not live long enough.

use std::io::BufRead; 

fn main() { 
    let stdin = std::io::stdin(); 
    let mut lines = stdin.lock().lines(); 
    let mut chars = lines.flat_map(|x| x.unwrap().chars()); 
} 

Điều này được đề cập trong Read file character-by-character in Rust, nhưng thực sự không giải thích được lý do.

Điều tôi đặc biệt nhầm lẫn là cách điều này khác với ví dụ trong documentation for flat_map, sử dụng flat_map để áp dụng .chars() cho vectơ của chuỗi. Tôi không thực sự thấy nó khác nhau như thế nào. Sự khác biệt chính tôi thấy là mã của tôi cần phải gọi unwrap() là tốt, nhưng thay đổi dòng cuối cùng để những điều sau đây không làm việc, hoặc:

let mut chars = lines.map(|x| x.unwrap()); 
let mut chars = chars.flat_map(|x| x.chars()); 

Nó không thành công trên dòng thứ hai, vì vậy vấn đề không xuất hiện là unwrap.

Tại sao dòng cuối cùng này không hoạt động, khi dòng tương tự trong tài liệu không? Có cách nào để làm việc này không?

Trả lời

6

Bắt đầu bằng cách tìm ra những gì các loại biến của việc đóng cửa là:

let mut chars = lines.flat_map(|x| { 
    let() = x; 
    x.unwrap().chars() 
}); 

Điều này cho thấy đó là một Result<String, io::Error>. Sau khi unwrap ping nó, nó sẽ là String.

Tiếp theo, nhìn vào str::chars:

fn chars(&self) -> Chars 

definition of Chars:

pub struct Chars<'a> { 
    // some fields omitted 
} 

Từ đó, chúng ta có thể nói rằng gọi chars trên một chuỗi trả về một iterator mà có một tham khảo để chuỗi.

Bất cứ khi nào chúng tôi có tham chiếu, chúng tôi biết rằng tham chiếu không thể sống lâu hơn điều mà nó được vay mượn. Trong trường hợp này, x.unwrap() là chủ sở hữu. Điều tiếp theo cần kiểm tra là nơi quyền sở hữu đó kết thúc. Trong trường hợp này, việc đóng cửa sở hữu String, do đó, vào cuối của việc đóng cửa, giá trị được giảm xuống và bất kỳ tài liệu tham khảo nào bị vô hiệu.

Ngoại trừ mã cố gắng trả về Chars mà vẫn được đề cập đến chuỗi. Rất tiếc. Nhờ Rust, mã đã không bị phân đoạn!

Sự khác biệt với ví dụ hoạt động là tất cả trong quyền sở hữu. Trong trường hợp đó, các chuỗi được sở hữu bởi một vectơ bên ngoài vòng lặp và chúng không bị loại bỏ trước khi trình vòng lặp được tiêu thụ. Do đó không có vấn đề suốt đời.

Mã này thực sự muốn là phương pháp into_chars trên String. Trình vòng lặp đó có thể sở hữu giá trị và trả về các ký tự.


Không hiệu quả tối đa, nhưng một khởi đầu tốt:

struct IntoChars { 
    s: String, 
    offset: usize, 
} 

impl IntoChars { 
    fn new(s: String) -> Self { 
     IntoChars { s: s, offset: 0 } 
    } 
} 

impl Iterator for IntoChars { 
    type Item = char; 

    fn next(&mut self) -> Option<Self::Item> { 
     let remaining = &self.s[self.offset..]; 

     match remaining.chars().next() { 
      Some(c) => { 
       self.offset += c.len_utf8(); 
       Some(c) 
      } 
      None => None, 
     } 
    } 
} 

use std::io::BufRead; 

fn main() { 
    let stdin = std::io::stdin(); 
    let lines = stdin.lock().lines(); 
    let chars = lines.flat_map(|x| IntoChars::new(x.unwrap())); 

    for c in chars { 
     println!("{}", c); 
    } 
} 
+0

Ah, tôi thấy, cảm ơn! Tôi cho rằng những gì tôi đã nhầm lẫn là thực tế là tất cả các chức năng xử lý với 'Chuỗi' thay vì' & str', vì vậy có vẻ như nó sẽ di chuyển các giá trị. Nhưng đó không phải là trường hợp vì các bao đóng không trả lại các giá trị thực, mà là các trình vòng lặp mà sau này được đánh giá một cách lười biếng, và các trình vòng lặp đó chứa các tham chiếu đến đối tượng ban đầu. –

+2

@ IanD.Scott Trong khi 'chars' có thể được gọi trên một' String', lưu ý rằng nó có '& self' (một tham chiếu) và nó thực sự được thực hiện thông qua' Deref', có nghĩa là việc thực hiện thực sự là trên 'str'. Do đó '& self' =>' & str'. – Shepmaster

+0

Cảm ơn bạn đã lừa 'let() = x;' để xác định loại biến! –

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