2016-11-01 3 views
5

stdinで文字を繰り返し処理しようとしています。 Read.chars()メソッドはこの目標を達成しますが、不安定です。明らかな代替方法は、をflat_mapとし、それを文字イテレータに変換することです。.chars()を使用した.flat_map()がstd :: io :: Linesでは動作しないのはなぜですか?Stringのベクトルではどうなりますか?

これはうまくいくはずですが、そうでないと、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()); 
} 

これはRead file character-by-character in Rustで述べたが、それは本当に理由を説明does'tされます。

特に私が混乱しているのは、を使用して文字列のベクトルに適用するdocumentation for flat_mapの例とどのように違うかです。私はそれがどのように異なっているべきかを本当に見ていない。私が見る主な違いは、どちらかの私のコードは、同様unwrap()を呼び出す必要があるということですが、次のように最後の行を変更することはできません:

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

それは二行目で失敗したので、問題は表示されません。 unwrapとなります。

なぜこの最後の行が機能しないのですか?ドキュメントの似たような行はなぜですか?これを動作させる方法はありますか?クロージャの変数の型が何であるかを考え出すことで

答えて

6

スタート:

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

これは、Result<String, io::Error>だ示しています。 unwrapをpingした後は、Stringになります。

次に、str::charsを見て:

fn chars(&self) -> Chars 

そしてdefinition of Chars

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

ことから、我々は、文字列にcharsを呼び出すことに参照を持つイテレータを返すことがわかります文字列

参考資料がある場合はいつでも、参考資料は借りているものよりも長生きすることはできません。この場合、x.unwrap()が所有者です。次に確認するのは、その所有者の終了位置がの場合です。この場合、クロージャはStringを所有しているため、クロージャの最後に値が破棄され、参照は無効になります。

コードを除いて、まだ文字列を参照しているCharsを返そうとしました。おっとっと。 Rustのおかげで、コードはsegfaultしませんでした!

動作する例との違いは、すべて所有権にあります。その場合、文字列はループ外のベクトルによって所有され、イテレータが使用される前に削除されません。したがって、生涯の問題はありません。

Stringにはinto_charsメソッドがあります。そのイテレータは値の所有権を取り、文字を返すことができます。


ない最大の効率が、良いスタート:

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

ああ、私は、感謝を参照してください!私が混乱していたのは、 '&str'ではなく' String'で関数がすべて処理されているということです。値を動かす必要があるようです。しかし、クロージャが実際の値を返すのではなく、遅れて評価されるイテレータと、それらのイテレータが元のオブジェクトへの参照を含むので、そうではありません。 –

+2

@ IanD.Scott '文字列'に対して 'chars'を呼び出すことができますが、'&self'(参照)を受け取り、実際に 'Deref'を介して実装されていることに注意してください。したがって、 '&self' =>'&str'です。 – Shepmaster

+0

変数型を決定するための 'let()= x;'トリックをありがとう! –

関連する問題